In general I prefer
inheritance. That has several reasons:
Humans are bad at dealing with complexity. And dealing with high inheritance trees is complexity. I want light structures on my brain, which I could overlook easily. The same goes for dozens of types even derived from one base class.
You are able to switch moving parts out. You could have a simple device, which does
runOperatingSystem(), instead of making two different
device-classes, you make only one and give it an
Operating System. If you want to test behavior, you could inject a
mockOperatingSystem and see, if it does, what it should.
You are able to extend behaviour, simply by injecting more behavioral components.
In terms of
abstraction, you are better off, designing a generic device type:
In Python the design would look like the following
def __init__(self, OS, name):
You have a generic
device which runs an
operating system. Every userinteraction is delegated to this
OS. Perhaps you want to test only the browsing call dependent on any operating systen, you could easily swap it out.
The next step for this design would be, to create a
configuration-object, which takes the common parameters (
HardwareId and so on). Inject this
configuration and the appropriate
OS via constructor injection and you are done.
You define common behaviour in a contract (
interface), which determines, what you could do with a phone and the operation system deals with the implementation.
Translated to everyday language: If you text with your phone, there is no difference in doing it with an iPhone, Android or Windows in that respect, that you are texting, although the mechanisms from OS to OS differ. How they deal technically with it is uninteresting for the device. The OS runs the
texting app, which itself takes care of its implementation. It is all a question of abstracting commonalities.
On the other hand: this is only one way of doing it. It depends on you and your model of the domain.
From the comments:
But you have to agree with me that this would require one to create a lot of wrapper methods to make the API simpler
This depends on what exactly you want to model. To extend the given example of
Say, you simply have some basic jobs, you want the device to do, you define an
API for that; in our case simply the method
sendSMS(text).You have then the
Device, where the message
"send text" is called upon. The
device in turn delegates that call to the used operating system, which does the actual sending.
If you want to model more than a handful of
device offers, you have to make bloated
This is a sign, that your abstraction level is too low.
The next step would be to abstract the concept of an app out of the system.
In principle, you have a
device which interacts with the inner logic of an
app, which runs on an
operating system. You have
input, which is processed and changes the
display of the device.
In terms of MVC, e.g. your keyboard is the
controller, the display is the
view and there is the
model within the app, which is modified. And since the view observes the model it reflects any changes.
With such an abstract concept, you are very flexible to build / model a lot of use cases.
I also am not seeing exactly how you would handle the operations concept here. There are still many types of operations, each with differing parameters, that can or can't be applied to a given device.
As said above:it is all a matter of abstraction. The more power you want, the more abstract your model has to be.
Where would the OS be located now after this abstraction?
Taking the example further, you have to develop several abstractions / patterns, which help in this case.
Mediator-Pattern: The operating system acts as a mediator, i.e. it takes signals in form of
commands, sends it to the app and takes in response
commands to e.g. update the view
Command-Pattern: The command pattern is the form of abstraction, which is used to describe the communication flow between components. Say, the user presses
A, than this could be abstracted as
Keypressed-command with a value of
A. Another command would be
update display with the value of
keypress.value or in this case
MVC The display as the
view, the keyboard as the
controller and in between the (app-)model.
Your imagination is your limit.
This is the kind of stuff, OOP was invented for originally: simulating independent components and their interaction