5

A project I am working on currently models Order information. Right now, there is one class with a bunch of properties and functions that all of orders have in common. In addition, some of our users have customized properties and/or functions on top of the base order. Some of those customized properties are shared between multiple users and some are unique to a given user. The class has nearly 50 different properties, but a given user might only be using 20 or so of those properties.

I'd like to redesign it so that the base properties/functions are separate from the customized properties/functions, but I don't know how to approach this. I thought about inheriting the base class, but a user might have 3 or 4 different customized properties. There is no clear inheritance among the customized properties. I looked into the decorator pattern, but the interface is changing, so that doesn't seem to fit.

Is there any way to accomplish this without having the monolithic class?

Examples:

Base Properties - OrderID, OrderDate, AccountId, etc.
Customized Properties - BackOrdered, PONumber, ContactPerson
Base Functions - PlaceOrder, CancelOrder
Customized Functions - AddContact, DiscountPriceByPercent

So, some users might only have the base, one user might have the base + BackOrdered, and another user might have base + BackOrdered + PONumber.

This problem isn't unique to orders, I have the same problem with other objects in the project. I'm looking more for an architecture/pattern to solve the general case than for a way to handle orders.

  • 1
    Can you give some examples of the differebt functions/properties? – Ewan Dec 16 '15 at 0:39
7
+50

Sounds to me that the custom properties are about order processing state.

Make a state diagram of your order process. Design classes that move the Order from one state to another, for example from a backordered state to a shipped state. The core Order would be contained w/in each as it is going through that particular state change.

There may also be a class that captures the various states an order has gone through as it goes from a new order, filled, paid, shipped, et cetera. Whether this "checklist" is an integral part of the Order class is a matter for your design analysis to resolve. There may be an OrderProcessor that drives an order through the state labyrinth.

P.S.

My answer sounds quite similar to @Ewan answer, e.g. "order can have different things happen to them" However I want to emphasize the states with transitions between them explicitly defined as opposed to discrete "services" with no defined relationships.

In other words to ship an order it must be in a certain state (filled, let's say) and by design we know it went through all the required states up to the current state. Otherwise every unrelated "service" would have to evaluate the order for its entire state-chain, potentially sending it back to "fix it" - a potentially recursive bowl of mush.

  • +1, the different workflows have to be state machines to avoid a mush tarpit. – Ed Plunkett Dec 20 '15 at 21:29
  • I don't think this answer really applies in my case, but is the best answer for how to deal with Orders in the general. – KevenDenen Dec 22 '15 at 20:44
  • 1
    @KevenDenen, Fussing w/ individual properties in this way suggests that current design is inadequate. Properties are state. What is being done that requires state change? Think hard about how the 50 state class is likely a composition of classes each doing their own thing, contributing to the whole. A class is about doing and properties facilitate doing. When you're clear about what to do the properties should coalesce around that. Then you can apply the state pattern (discussion and code) – radarbob Dec 22 '15 at 21:29
1

If all of the custom properties are known at compile time, but they can be combined in different ways and you don't want to produce large hierarchy of classes, then tou can use Mixed pattern, which implementation will depend on the programming language you use. Code example on Java:

interface Mixed {
    <M, I extends M> void extend(Class<M> iface, I impl);
    <M> M as(Class<M> iface);                
}
class MyObject implements Mixed {
     String getP1();
     LocalDate getP2();
}
class CustomFeature1 {
      String getA1();
      String getB1();
}
class CustomFeature2 {
      String getA2();
      String getB2();
}

// O will have properties P1,P2,A1,A2,B1 and B2
MyObject o = new MyObject();
o.extend(CustomFeature1.class, new CustomFeature1());
o.extend(customFeature2.class, new CustomFeature2());
o.as(CustomFeature1.class).getCustomPropertyA1();

If custom properties are defined at runtime, probably, it's time to think about runtime data structures defined by a metamodel:

interface Property {
    String getName();
    Class<?> getValueType();
    Object getValueFor(Object object);
}
interface Model {
     addProperty(String name, Class<?> valueType);
     List<Property> getProperties();
}
interface DynamicObject {
     Model getModel();
     Object getValue(String propertyName);
     void setValue(String propertyName, Object value);
}
class MyObject implements DynamicObject {
     String getP1();
     String getP2();
}

MyObject o = new MyObject();
// add new custom property
o.getModel().addProperty("A1", String.class);
// set custom property value
o.setValue("A1", "Lorem Ipsum");

// Let's list all custom properties and their values
for (Property property : o.getModel().getProperties()) {         
     System.out.println(property.getName() + "=" + property.getValueFor(o));
}

In both solutions you'll need to implement custom persistence mechanism to store the data in database or to serialize in XML or JSON. You'll need to use reflection for that.

  • I don't see much about the mixed pattern, but in researching this, I ran into a couple options that we are floating as possible solutions! – KevenDenen Dec 22 '15 at 20:48
0

For order fulfilment processes where differnt types of order can have different things happen to them I suggest that you take a service orientated approach. Moving the domain logic out of the object into services and add an event history to the object to store its variable state.

Ie. Instead of AdvanceOrder and SendNowOrder with overridden Buy() methods have BuyNowService and PlaceAdvanceOrderService both of which have a Buy(Order order) method.

You can then send orders to one or other service as required.

Instead of AdvanceOrder.DateToPlace and BuyNowOrder.IsInStock Have DelayUntillEvent and OutOfStockEvent inheriting from a base Event and stored in Order.Events Only services which know how to handle these events need reference them.

0

If performance is the main goal, then i would go for the monolithic solution. If flexibility is the one, then may be you can use a dictionary called 'CustomProps' with the name and value of the custom properties and another called 'CustomFuncs' with the name and function you want to call.

Now you can make a getter/setter with a string name and a object value, find the property and cast the value. For the functions analog with a string name and an function/action/delegate, that you can call. If they need parameters, this can be enhanced for it.

Clearly, this will not be the fastest, but very flexible...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.