How to organize-class/object relationship

I

ishijak

I have to construct a service that client programmers will use,
eventually, client programmer can see the service through one interface
and one class that will create and return references to particular
implementation of that interface.

public interface Customer
{
// decreases the customer account by value of volume
void getMoney(double volume)

// increases the customer account by value of volume
void putMoney(double volume)

// returns unique customer id
String id();
}

public class CustomerCreator
{
// creates particular impl. of the Customer based on id
public Customer createCustomer(String id);

public void destroy(Customer cust);
}


// handles Customer whose id's are "3[0-9].*"
// This is a facade to subsystem that handles the group of these
customers
class CustomerImplOfType3 implements Customer
{
public void getMoney(double volume){}
public void putMoney(double volume){}
public String id(){}
}


// handles Customer whose id's are "7[0-9].*"
// This is a facade to subsystem that handles the group of these
customers
class CustomerImplOfType7 implements Customer
{
public void getMoney(double volume){}
public void putMoney(double volume){}
public String id(){}
}

Based on "id" value, there are completely different
ways(implementations) for handling customers and different subsystems
are involved. CustomerCreator has to decide from which implementation
to make instance. Customer implementations (Facades to subsystems) will
increase over time and existing ones will change. Also I want to have
possability to implement new CustomerImpls and to do not have to
recompile neither the framework classes, neither the existing
implementations (if they're still valid).
CustomerCreator.createCustomer will throw "Notsupported" if there is no
impelmentation that can handle given id.


One approach is to create an array of prototypes of CustomerImpl, all
of them will implement boolean canYouHandle(String id) and the
CustomerCreator object shall iterate and ask every prototype, and to
clone that one that will answer "yes(true)"

So the implemenatation will look something like this:


---charging framework package---

package chargingfw;

// Client programmers will use this interface to manipulate the
customers
public interface Customer
{
// decreases the customer account by value of volume
void getMoney(double volume);

// increases the customer account by value of volume
void putMoney(double volume);

// returns unique customer id
String id();
}


// subsystem programmers will use this interface to integrate
subsystems in the application
public interface CustomerFacadeBase extends Customer
{
public boolean canYouHandle(String id);
public CustomerFacadeBase instance(String id);
}

public class CustomerCreator
{
ArrayList<CustomerFacadeBase> custPrototypes;

public CustomerCreator(String[] listOfImpls)
{
for (each i in listOfImpls)
{
//load corresponding classes
// create prototype instance
custPrototypes.add(currenProto);
}
}


// creates particular impl. of the Customer based on id
Customer createCustomer(String id) throws Unsupported
{
for (each i in custPrototypes)
{
if (i.canYouHandle(id))
return i.instance(id);
}
throw new Unsupported(id);
}

void destroy(Customer cust);
}


---Subsystem3 Facade Implemenation package---

// handles Customer whose id's are "3[0-9].*"
// This is a facade to subsystem that handles these customers
final class CustomerImplOfType3 implements CustomerFacadeBase
{

public CustomerImplOfType3(String id)
{
_id = id;
init_subsystem();
}

public void getMoney(double volume){}
public void putMoney(double volume){}
public String id(){}

public CustomerFacadeBase instance(String id)
{
return new CustomerImplOfType7(id);
}


public boolean canYouHandle(String id)
{
if (id like "3[0-9].*")
return true;
return false;
}

private Subsystem3 ss;

}

---Subsystem7 Facade Implemenation package---

// handles Customer whose id's are "7[0-9].*"
// This is a facade to subsystem that handles these customers
final class CustomerImplOfType7 implements CustomerFacadeBase
{
public CustomerImplOfType7(String id)
{
_id = id;
init_subsystem();
}

public void getMoney(double volume) {}
public void putMoney(double volume){}
public String id(){}


public CustomerFacadeBase instance(String id)
{
return new CustomerImplOfType7(id);
}

public canYouHandle(String id)
{
if (id like "7[0-9].*")
return true;
return false;
}

private Subsystem7 ss;
String _id;
}


It would be better if only one object instance per customer exists in
the application, and all manipulation to go through that inctance for
particular customer.


---client code---

// client code shall be multithreaded,

import chargingfw;

main()
{
String[] ssFacades = //subsystem facade class names
CustomerCreator cr = new CustomerCreator(ssFacades);

Customer cust = cr.createCustomer("3124455");
cust.getMoney(10);
...
...
cr.destroy(cust);

}

My questions are:
Is this approach good?
Any suggestions for better design?
Issues?

Thanks,
Ivan Sijakovski
 
R

Robert Klemme

ishijak said:
I have to construct a service that client programmers will use,
eventually, client programmer can see the service through one interface
and one class that will create and return references to particular
implementation of that interface.

public interface Customer
{
// decreases the customer account by value of volume
void getMoney(double volume)

// increases the customer account by value of volume
void putMoney(double volume)

// returns unique customer id
String id();
}

public class CustomerCreator
{
// creates particular impl. of the Customer based on id
public Customer createCustomer(String id);

public void destroy(Customer cust);
}


// handles Customer whose id's are "3[0-9].*"
// This is a facade to subsystem that handles the group of these
customers
class CustomerImplOfType3 implements Customer
{
public void getMoney(double volume){}
public void putMoney(double volume){}
public String id(){}
}


// handles Customer whose id's are "7[0-9].*"
// This is a facade to subsystem that handles the group of these
customers
class CustomerImplOfType7 implements Customer
{
public void getMoney(double volume){}
public void putMoney(double volume){}
public String id(){}
}

Based on "id" value, there are completely different
ways(implementations) for handling customers and different subsystems
are involved. CustomerCreator has to decide from which implementation
to make instance. Customer implementations (Facades to subsystems) will
increase over time and existing ones will change. Also I want to have
possability to implement new CustomerImpls and to do not have to
recompile neither the framework classes, neither the existing
implementations (if they're still valid).
CustomerCreator.createCustomer will throw "Notsupported" if there is no
impelmentation that can handle given id.


One approach is to create an array of prototypes of CustomerImpl, all
of them will implement boolean canYouHandle(String id) and the
CustomerCreator object shall iterate and ask every prototype, and to
clone that one that will answer "yes(true)"

So the implemenatation will look something like this:

My questions are:
Is this approach good?
Any suggestions for better design?
Issues?

If you have only a small set of sub systems, then I'd probably just have
that many createCustomerTypeX(String id) methods and determine the
appropriate method programmatically "if (id.startsWith("3") ... else
if...".

If it's more complex I'd have a factory for each subsystem and class
CustomerCreator (better CustomerFactory) just delegates to the appropriate
subsystem factory; the simplest approach would be to just iterate through
all factories and ask each in turn whether it feels like handling this
particular id (for creation) or instance (for deletion).

You might also consider to put the delete method into the Customer
interface because the customer can easily know its factory. Drawback is a
certain degree of asymmetry between creation and deletion.

Kind regards

robert
 
K

kurbylogic

I think that is a reasonable approach.

Another option you might concider is meta data.
Instead of i.canYouHandle(String Id)
you could use configuration files to associate an id or pattern with a
typename.
<customerTypes>
<add pattern="3[0-9].*"
typeName="CustomerImplOfType3"/>
...
</customerTypes>

This could also become a source of (mis)configuration errors so
sometimes its not worth adding flexibility where it isn't needed.
However in this case perhaps it is appropriate, expecially with "magic
numbers." Today it is 3[0-9], but tomorrow when decide to convert all
there existing data to use an alpha prefix instead it becomes C[0-9],
not only do you have to modify the code its also distributed across
many class files. If you decide it is so unlikely to change you would
rather modify the source code than maintain config files then using a
if/else/switch statements in the factory would at least centralize the
type mapping functionality.
canYouHandle does not seem complex enough in this scenario to
distribute the function to the various individual objects. If logic
begins to get more complex and/or seperate properties are used to
determine membership than asking the object canYouHandle might be more
appropriate, and you should make canYouHandle virtual so if it for
example you find that 3[0-9]* is actually 3([0-9]|[A-Z])* you can
simply replace this method instead of creating a new implementation.

- Kurt
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top