3

I am working on implementing some stock order types for a financial technology application. There are six different types of stock orders - market, limit, stop_loss, stop_loss_limit, trailing_stop, and trailing_stop_limit. These orders share a minimal subset of properties like IsBuyOrder and TimeInForce. However, they each add their own unique properties as well. For example, stop_price is only relevant to stop_loss and stop_loss_limit order types, and trailing_stop_amount is only relevant to trailing_stop and trailing_stop_limit orders.

As I see it, there are two implementations I can choose from to implement this.

  1. I can have one relatively large order class with a lot of nullable properties. This class would exposes all of the fields of all the various order types. Many of these fields would be null as they might not apply to the specific type of order that is being created. The advantage of this solution is it's shorter than #2 below, the disadvantage is the class isn't as easy for consumers to understand and utilize.
  2. I can have an abstract base class called Order which contains the properties common to all order types, and have six child classes implementing the specific order types. The advantage of this solution is it's more clear to the consumer of the class what properties they must set in order to get their order filled. The disadvantage is this solution is quite long as I will have seven different classes.

Is #2 better than #1? Is it better to have a large inheritance hierarchy or a single object with lots of null fields? Just looking for some guidance here...

13
  • 2
    I think you're making a mistake that fundamentally misunderstands OOP. It's not about properties, encapsulation inheritance, saving yourself the repetition of defining a few fields with the same name/type, or whatever. It's about polymorphism. Don't focus at all on properties of the objects. Focus on what methods you would like order actions to perform. Things like isExpired(), cancel(), fulfill(), or whatever else. From there, decide where the properties make sense (or if inheritance is even necessary, or if a simple interface could do)
    – Alexander
    Sep 13, 2022 at 16:43
  • 1
    The objects I am modeling will have no methods. They are just data transfer objects. They will be created by the user and passed to the order execution layer of software.
    – Treker
    Sep 13, 2022 at 16:47
  • Out of curiosity, can you elaborate on what programming language you are using?
    – WMRamadan
    Sep 13, 2022 at 16:48
  • 6
    if there are no methods, then this is a data modelling problem where object-orientation and particularly inheritance can't help you, since OO is about methods/behaviour rather than data. The kinds of things to think about with data modelling will depend upon the data formats where you use and store the data - e.g. if it's being stored in a SQL database then it'd be useful to think about 3rd normal form Sep 13, 2022 at 16:50
  • 2
    @Treker Its not OOP that is helping you here, it is just simple inheritance. You can have inheritance without OOP. Really, your question/problem have no connection to OOP at all, since you are just returning data objects which can be done from non-OOP languages. I don't think it matters greatly, but I would also recommend that you use solution #2 (inheritance and less null fields).
    – GHP
    Sep 13, 2022 at 17:32

3 Answers 3

4

The option 1 looks appealing as it could seem simpler. However it comes with numbers of drawbacks:

  • The design does not conform with the open/closed principle: every time, you'd add a new order type, you'd need to modify the code and you might break things that worked well before.
  • Your code will need a lot of if/else or switch/case to account for the different variations. It is error error-prone (you might forget a case) and difficult to maintain.
  • The code is data-driven and not object oriented: you query data and do things with it, instead of telling the order to do something and let the order deal with the specific details (tell don't ask). Moreover, this might constraints encapsulation, and lead to hidden coupling that will also make maintenance difficult.
  • Over time, due to the missing abstraction, it'll easy to lose connections. For example it'll be easy to forget that stop_price is relevant to stop_loss and stop_loss_limit and not the others.

In summary, you may get faster with option 1 in the short run, but you'll pay a hard maintainenance price in the long run.

The option 2 looks more promising. In fact, I assisted in the 90's to a series of conference about object oriented technologies in financial services, and your example was comonly used to promote inheritance and the ease it gives to innovate with new financial products.

Nowadays, we know that deep order hierarchies are in fact also coming with a cost. So you may want to consider Option 3:

This may further allow to break down the strategies in smaller pieces, i.e. stop price or not, start price or not, timing or not, and combine a few basic strategies to achieve more complex strategies without redundant code.

3
  • Nice answer, thank you. Can you elaborate a bit more on the costs of deep order hierarchies? And when exactly is composition preferable to inheritance?
    – Treker
    Sep 13, 2022 at 21:25
  • 1
    @Trecker the problem with deep hierarchies is that it is often caused by combinatorial explosion: several criteria drive the subclassing, and you need then many classes. If the criteria are orthogonal, you might have to either repeat some code or use multiple inheritance, which is not easy to master. Moreover you lust anticipate combinations at compile time. Then, inheritance is something static for objects in most programming languages: you must know the type of the object when you create it, and cannot change it afterwards. omposition allows you to change some behavior at runtime and
    – Christophe
    Sep 14, 2022 at 6:52
  • 1
    facilitates combination of orthogonal behaviors.
    – Christophe
    Sep 14, 2022 at 6:53
1

Option 1 allows nonsensical objects (combinations of properties that are meaningless) and it wastes memory. This is not good. Option 2 (use inheritance) is better, it provides guidance and does not allow nonsense.

Instead of having an IsBuyOrder property you could implement IBuy, declare it as implemented and test for that implementation when you want to know if it's a buy order. And do something similar for other types. This allows you to have processing methods for different order types that take an interface as an argument which is as type safe as can be.

0

Basically what @Alexander said, only he gave up for some reason :) There are no such things as objects without behavior, or methods. Data Transfer Objects do not exist in OO, nor, and this might be a better argument, in maintainable code.

Also, I did actually work on a trading middleware software, where I did something very similar to what you describe.

My polymorphic behavior was execute(). When called, the Order contacted the backend system and filled out the order as entered by the user. There were several order types, similar to yours, which did slightly different things. We also had a completely different market type (OTC), where we did not contact our backend, but another institution. All of this was "hidden" behind the simple polymorphic call of execute().

Anyway, my point is pure data structures make code brittle. You can't change it without tracking down who uses it and for what. Use proper object design. Encapsulate data, find the behavior that you actually really need.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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