So I have written my first Python application of more than trivial size, and I did it procedurally because that was the information I had best absorbed to date. I now want to refactor it to try and (finally) understand classes and OOP. My question is about the best practices for designing classes as one develops from a simple beginning.
So let's say I have a application that simulates an investment portfolio of short equity put options. This means at any given time I could have a portfolio (container) composed of holdings of short put options, as well as long stocks (from short options that were later exercised/assigned).
So in terms of nouns there is a lot going on. There are data/attributes/methods applicable to any any equity option, data/attributes/methods that are specific to put options (vs call options), and data/attributes/methods that are specific to short put options (vs long put options). I could conceptually have:
class Option(object):
"""Describes an Option"""
pass
class Put(option):
"""Describes an Option that is a Put"""
pass
class Call(option):
"""Describes an Option that is a Call"""
pass
class ShortPut(Put):
"""Describes a Put that is written (sold short)"""
pass
class LongPut(Put):
"""Describes a Put that is purchased"""
pass
class ShortCall(Call):
"""Describes a Put that is written (sold short)"""
pass
class LongCall(Call):
"""Describes a Put that is purchased"""
pass
# then more classes to describe stocks, holdings of a
# given quantity of a stock or option at a specific price
# on a specific date, and a portfolio to contain all the
# holdings etc etc
I am well on my way to a complete taxonomy. Maybe there is too much inheritance, which I have read can be a bad thing. But it does describe an object and allows for differing behavior/data as additional/different attributes are added. However, it feels like I have a huge amount of scaffolding that does nothing because I can describe everything I need at this stage with just a ShortPut object.
Also, I have been focused very much on learning good unit testing, keeping things simple, and building as you go. So in reality I think it should look like this:
class ShortPut(object):
"""Describes a short put"""
pass
That is the only kind of option I have in my simulation at the moment, so I think of this as the "interface." It seems to me that if I encapsulate this object correctly, then I can start with just this one object and wait to refactor into some larger taxonomy only as warranted. For example, if I later need a long put, then I would be repeating a put description inside this new object and that would violate DRY. So instead I would create a higher-level (base?) class to hold the common code, and I would describe the differences in behavior with separate inherited classes:
class Put(object):
"""Describes an Option that is a Put"""
pass
class ShortPut(Put):
"""Describes a Put that is written (sold short)"""
pass
class LongPut(Put):
"""Describes a Put that is purchased"""
pass
And then I can just continue with that recursive process as my descriptions and use cases become more complex. But, if I encapsulate correctly, my goal should be to never need to adjust any of the "downstream" code - when I want a ShortPut I should be able to keep making the same request regardless of how complicated and distributed it gets under the hood. Finally, everything in my code actually does something related to the intended outcome, and so unit tests are meaningful to the outcome.
Am I thinking about this problem correctly? It seems obvious to me, but then I have had to rip up a lot of what I written in the past because what I assumed to be "obvious" turned out to be dead wrong. I apologize if this seems too basic or if I don't have all the terms down exactly - I am still learning. Everything I have been reading has focused on short examples of how to design classes and illustrate concepts like inheritance or overloading, but I haven't come across as much about how to design simply with an eye towards future complexity or extensibility. Thanks!