Can Liskov Substitution Principle be applied (php example)? - oop

"Subtypes must be substitutable for their base types"
Let's say I have a Bird class already, and:
class Parrot extends Bird {
public function Talk() {
echo 'Talk';
}
}
A Bird can't talk so I can't substitute a Parrot with a Bird.
This is just a basic example but usually the extended class can do much more then the base class. What am I missing?

The point is a parrot should act like a bird in all ways, so that someone who has a plan for dealing with birds in general won't be surprised by a parrot they run across. If the parrot happens to be able to talk as well, it doesn't matter, because their plan doesn't involve asking birds to talk.
For example, perhaps they just call $bird->fly() on every bird they get - the ability of a parrot to talk won't disrupt that activity, so their algorithm still works. But if you invent a sort of bird that can't fly (an ostrich, say), then you've violated the principle, and their general bird-handling algorithm no longer works on all sorts of birds.

Related

Characteristics of bad object oriented design

I am reading about object oriented design principles. I came across the characteristics of a bad design.
It is hard to change because every change affects too many other
parts of the system. (Rigidity)
When you make a change, unexpected parts of the system break.
(Fragility)
It is hard to reuse in another application because it cannot be
disentangled from the current application. (Immobility)
I am able to understand the first two but the third one is little difficult for me to understand.
Is it about extracting common features of related classes in a Base class, making methods from the code which is repetitive ? But it says hard to reuse in another Application. Usually we write context specific code and Over-engineering is not a good idea, we have good principles like YAGNI (You ain't gonna need it) I find these ideas little contradicting.
Please provide your valuable thoughts for this.
Mobility example:
Assume the following classes:
Animal
Canine
Dog
As you would expect, Canine extends Animal and Dog extends Canine.
One way to poorly design Animal is to give it a method talk() which prints out bark. Perhaps, the original intent of this application was for dogs only and therefore the talk method barking was fine. But reusing this in another code base would lead to issues.
Say we want to extend Animal and create Bird. Birds don't bark :)
It's hard to image that someone would do this. But it happens all of the time. Base classes aren't abstracted out which leads to misplaced code making it difficult to correct/reuse.
One could argue that the Bird class could override the talk method. That would work, however, another developer extending Animal for yet another reason may forget to override that method... etc.
I know this isn't the best example, but it demonstrates the problem.

How does the single responsibility principle relate to cats and dogs?

From Wikipedia:
Martin defines a responsibility as a reason to change, and concludes
that a class or module should have one, and only one, reason to
change.
However, every time you see an object-oriented analogy, they refer to cats and dogs, and how they have attributes and methods like so:
class Dog extends Animal{
public breed;
public dogYears;
public function bark();
public function move();
public function sleep();
etc, etc...
}
I find a better way to implement bark, so I do that. I see that the dog doesn't move properly, so I fix that. Etc etc. There are more than one reason to change the class.
So, why is this analogy always used? What would be a better way to adhere to the SRP?
Don't take it so literally.
In your example, Dog is responsible for performing real life "dog actions", such as barking, moving and sleeping. You can see this as one responsability. The SRP states that Dog should not be responsible for perming any non-dog actions, such as logging his movements.
This prevents you from having to change the Dog class if you decide to change the logging mechanism - because performing "dog actions" and logging are two completely different responsibilities.

Is Car class violating Single Responsibility Principle?

Even thought I think I understand Single Responsibility Principle and high/low cohesion principle, the following questions are still causing me some confusion
1) Assume Planet and Bird properties are put arbitrarily / at random in Car class ( ie. no code within Car needs or operates on the two objects returned by these two properties ) - in other words, Planet and Bird properties don't belong in Car class
a)
SRP states that object should have only one reason to change.
public class Car
{
public void StartEngine()
{ ... }
private Planet Planet
{
get { ... }
}
private Bird Bird
{
get { ... }
}
}
Is Car class violating SRP? I would say it doesn't break SRP, since any changes to Planet or Bird instances don't propagate to the Car class?
b)
Cohesion refers to how closely related methods and class level
variables are in a class. In highly cohesive class all the methods
and class level variables are used together to accomplish a specific
task. In a class with low cohesion functions are randomly inserted
into a class and used to accomplish a variety of different tasks
Assume that even though Car class contains these two random properties, it still accomplishes just a single specific task ( or several closely related task ):
Would we say that Car has low cohesion, even though it still performs a specific task ( or several closely related tasks )?
2) Assume that Planet and Bird properties are used by methods of a Car instance to accomplish a specific task, would then Car have high cohesion, even though conceptually the two properties don't belong to Car ( and thus it would be better if instead Planet and Bird instances were passed as arguments to a methods of a Car which operate on them )
thank you
HELTONBIKER:
1)
as you encapsulated Bird and Planet inside Car (worse yet if they are
private), so now Car class has THREE reasons to change:
I fail to see how Car has three reasons to change since in my first question Car's methods don't even operate on the two properties and thus any changes to Planet's and Bird's public API won't affect Car class?
2)
The problem here has two components:
1. Bird and Planet are contained (as opposed to aggregated) in Car class;
2. Bird and Planet are not conceptually related to Car, much less by some containment relationship.
a) This is confusing: aren't the chances ( at least with my first question ) of Car having to be modified due to modification of Planet or Bird instances exactly the same regardless of whether Planet and Bird instances are contained or aggregated?
b) In second question methods of Car do operate on the two properties in order to perform a single specific task, so aren't they conceptually at least somewhat related? Would you say that even in second question class has low cohesion, even though it performs only a single task ( and is using the two properties to accomplish the task )?
The car class does have low cohesion, as it refers to classes wholly dissimilar to it's set of responsibilities. It also has a higher coupling surface, because since Planet and Bird are public, you've provided access to consumers to these properties, meaning that you're now adding two more "reasons for change" to any consumer, regardless of whether or not Car uses these internally.
At any rate, SRP has been violated if only because Car now has the responsibility of "a way to get planets or birds", disregarding any coupling/cohesion arguments.
1)
I would say that Car cannot hold Planet and Bird. That way Car has two different responsibilities: car functionality and holding some strange objects.
There should be some other object/class that would hold objects in world: eg: class WorldContainer
2)
I would say that both of your examples have low cohesion. Managing car and some different objects should be done using some other interface. Interface that would glue them together.
SRP means that a class should have only one reason to change.
So, a modification in Car class should mean that the Car conceptual model changed.
BUT, as you encapsulated Bird and Planet inside Car (worse yet if they are private), so now Car class has THREE reasons to change:
Modifying the Car conceptual model and/or behaviour;
Modifying the Bird conceptual model and/or behaviour;
Modifying the Planet conceptual model and/or behaviour;
The problem here has two components:
Bird and Planet are contained (as opposed to aggregated) in Car class;
Bird and Planet are not conceptually related to Car, much less by some containment relationship.
Or, plainly speaking (and I hope you did so as a didactic exercise), the architecture shown simply doesn't make sense.
Example with aggregation (in Python). The non-cohesive classes are defined outside the Car class definition, which references them. Car depends from Bird and Planet, but now Bird and Planet exist on their own.
class Bird():
hasWings = True
class Planet():
isFlat = False
class Car():
owner = Bird()
substrate = Planet()
Example with parameter-passing (just the car class, suppose the other classes are similar as above). Now the Car constructor (the __init__ method in python) takes instances as parameters. This might or might not be prefered. The dependency and coupling remains, but perhaps more concrete now.
class Car():
def __init__(bird, planet)
owner = bird
substrate = planet
In the end this whole issue of cohesion and coupling doesn't have so much to do with the software itself, but with the developers. Compilers won't mind if your namespaces, project folders and file distribution is messy, as long as it "compiles". But it wouldn't make ANY SENSE to do as you did (put a Bird and a Planet class inside a Car class). Just to begin, your versioning of each class would be very messed.
So, the purity you shouldn't violate is not that written in books for the sake of it. This purity is (or should have been) derived of human beings struggling with machine instructions. Object-Orientation, and Software Architecture in general, is not intended for the machine, but for the developer's (piece of) mind.

Musing/open questions on OO philosophy

I'm going to use this opportunity to put down a number of related but different thoughts on object-oriented philosophy, as a sort of request for comments. If the admins see fit to close, I may make it a blog post instead. But I am asking questions, so I think it's a suitable community wiki.
Suppose I have an abstract class Bird. Supposing that we're taking a fairly typical OO philosophy here, Bird is an object that is capable of maintaining and changing state. The obvious example of a state for this simple model is whether or not our Bird is flying or not.
So, Bird has a method fly().
Or does it? I've seen this very simple example given in introductory books, and in my opinion, it's subtly misleading. If you're maintaining state, you don't need to tell an object to continue to be in a given state; it just is. That's what an object does. So really, you want Bird to have a method takeoff().
Suppose at some point we created our Bird object from another class, Egg. At some point, we might call a method Egg.hatch() which has the return type Bird. Assuming we're coding to an interface, Egg knows what type of bird it's an egg from, and the returned instance from hatch() knows what type of bird it is also. But we can't really say that there's anything in common between an Egg and a Bird that hatches from it. are we really saying that we want specific implementations of these classes to have a reference to an instance of some sort of BirdArchetype that represents the characteristics of all instances of that species, so that both Egg and Bird know their own kind, independently but consistently? Is there any well-known pattern for this kind of relationship?
Whose responsibility is it to ensure that the Egg changes state so that it's no longer capable of hatching again, or doing most of the other things that eggs normally do?
Philosophically, do we want to introduce the concept of a breakable object? Perhaps Egg should be coded so that it throws an exception if you try to do any of the things that change its state more than once. It's up to the calling code to keep track of the client object's lifecycle.
What else can we do with an egg? Perhaps we could cook it. If it were me, I'd code this so that calling cook() on an egg 'breaks' the Egg instance and returns an instance of a new object, FriedEgg. How is this different to telling a Bird to takeoff()? It's absurd to suggest that taking off makes it a fundamentally different bird. Yet the reasoning is the same; a bird that's flying (usually) can't do most of the other things that birds do, because it's busy. Arguably then, Bird is breakable as while it's in the flying state, calling some of its other methods won't work.
I suppose the only real distinction here is that the Bird can become un-broken. Is the concept of thermodynamic reversibility really that crucial to programming very simple models like this?
This is why I don't particularly care for modeling the real world and using that to explain/define OO philosophy.
Unless you're building a simulator (in which the answers to these questions become immediately obvious when one looks at the simulator context) it's far too easy to go off into the weeds and confuse yourself.
Perhaps having mutating state in an object is all wrong ;-)
Shouldn't Bird.takeoff() return a FlyingBird which can land()? (It clearly doesn't make sense for a bird which is not flying to land.)
Similarly, for an egg:
egg = Egg()
incubatedEgg = egg.incubate()
bird = incubatedEgg.hatch()
However, the return type of an operation might not always be limited to one value. For instance, during incubating the embryo could die.
egg = Egg()
egg.incubate() ->
if "still alive" (aliveEgg) ->
aliveEgg.hatch()
else "died in incubation" (deadEgg) ->
deadEgg.discard()
Happy philosophizing :-)
"Fly" reasonably means "begin flying!" (I would say it's used more often that way in natural speech). You don't have to interpret it to mean "sustain flight!" That's why we reasonably use "fly" and "land" (in natural english) as antonyms. (Evidence for the skeptical)
You're assuming the egg is created ex nihilo in your model. That's not really a reasonable assumption. The bird came from an egg, but the egg came from a bird. Assuming a reasonable purpose for modelling an egg, it's to model the time-delay procreation of bird species. That means the baby bird is formed by the sexual reproduction of two other birds. The vehicle of generation of the baby bird is the egg. The egg doesn't need to know what kind of bird is in it; that bird was created by the parents and wrapped in a generic egg (that perhaps has variable properties based on the mother.
In other words, your model isn't really well-formed with regard to this question.
It's the egg's responsibility to see that it doesn't hatch again (a check in the hatch function). See the discussion of "imperatives" below.
The concept of "breakable" is probably superfluous to your model. Are you concerned with the egg's breakability, or it's hatchability? If the breaking is a significant part of the data you are trying to model (for example, if anything in your model essentially depends upon the brokenness of the egg, then model it. Either way, hatching doesn't happen when the egg breaks, it happens when the egg hatches. One of the consequences of hatching is breaking, not the other way around.
The reason egg.cook() seems wrong is because it is. Verb methods should take the object as the subject. Your statement "egg.cook()" is telling the egg to cook, which is rarely how we intend to use the verb "cook" in commands with respect to eggs. You probably really want a chef.cook(food) -- where the "food" arg is anything that derives from food (or better yet, has a Role (a la Moose) "isCookable".
Remember, programming languages are imperative languages; interrogation, exhortation, and other "natural language" functions are inherently misplaced in programming languages, even if we achieve some version of it through commands (we emulate interrogation through commands; "TELL ME YOUR STATE!" is the imperative interpretation of the interrogative "What's your state?")
A good program removes as much information as possible from it's models while meeting all functional specifications. That is, you have a specification (preferably in the form of a test suite). Meaningless information is information that isn't necessary to pass any of the tests. So, you implement one feature at a time, introducing the cookability of eggs when you have a test that quantifies that property via specification (again, read: tests). If you implement breakability of objects and realize that you can accomplish the same tests without it, you improve the program by removing it. Now, a really clever programmer is able to build his models in such a way as that they are extensible -- that is, that new properties can be added easily as new requirement specifications are produced.
Note also, it is possible to have a bad specification. If you can't tell whether something is superfluous or not, the specs (read: tests) are either incorrect or incomplete with respect to that aspect of the program.
Seems more like an issue of naming. For example, in java you might query the bird's state as "getFly()", where you call it "fly()" as python would. Here, you're telling it to take off like you're changing the state of the bird, which is a little ambiguous to the reader of the code.
In the case of the Egg, I don't see any problem with the species of bird the egg handles as being part of the egg. As for who handles whether the egg is hatched, you could just keep a reference inside the egg to the bird that's hatched. An egg physically hatches, so it's up to me to see if it's hatched, so I don't stick an egg shell in an incubator for months expecting a chicken to pop out. In my opinion, it's up to the calling code to know who has hatched. If an egg hatches a bird, it's okay to keep a reference to that bird inside the egg if that helps keep your program organized. I would think it would be a convenience to anyone who uses that class.
There's no reason why you can't make an egg also be in a fried state, where any attempts to hatch it fail.
As to the absurdity of a different object for a bird in flight being a different bird than a grounded one, that's hard to justify. We may reason in our minds that it's the same bird, but in respect to object-oriented programming, we could certainly make them different objects if that meets the needs of the program. Is a bird more like an egg or a plane?
As to your Bird being breakable while it's in flight, is that the bird's fault? If my dog were in it's kennel, would it be broken if I told it to go fetch a ball?
Whose responsibility is it to ensure that the Egg changes state so that it's no longer capable of hatching again, or doing most of the other things that eggs normally do?
This depends on whether you want to keep around the egg remains in your model or consider the egg gone.
If the former, the hatch() method sets a private hatched flag to true and any of the egg's methods which depend on it being un-hatched (including hatch() itself) check that flag and fail (whether the failure is via return code or a raised exception, depends). The latter, might be doable with certain designs but don't really see any need to.
Or does it? I've seen this very simple example given in introductory books, and in my opinion, it's subtly misleading. If you're maintaining state, you don't need to tell an object to continue to be in a given state; it just is. That's what an object does. So really, you want Bird to have a method takeoff().
I think that your problem here is with imprecise word usage, not with software development. The authors of the fly() solution are usng "fly" verb in a sense of "start flight", e.g. as a synonym of "takeoff". Whether that's a 100% valid English usage or not is above my pay grade as ESL developer, but I certainly agree that "takeoff" is a significantly less ambiguous method name.
... so that both Egg and Bird know their own kind, independently but consistently? Is there any well-known pattern for this kind of relationship?
I'm not sure if there's an official "pattern", but having an egg constructor (or factory if you go with factory) implemented in a generic "Bird" class pass the bird type to a new "Egg" object and vice versa having "Egg" object pass its bird type to the constructor (or factory) of "Bird" is very widely used.
What else can we do with an egg? Perhaps we could cook it. If it were me, I'd code this so that calling cook() on an egg 'breaks' the Egg instance and returns an instance of a new object, FriedEgg. How is this different to telling a Bird to takeoff()
As a commenter already pointed out, the above is an OO design problem - the egg doesn't cook itself. Therefore you can't implement "cook" method on the egg in isolation (though some "set status to cooked" method might be needed for your model, if for no other reason than make it unhatcheable. However, the FriedEgg thing needs to be constructed (if needed) by a Cook object's "cook_egg" method - which also calls "set status to cooked" on an egg.
I don't think this really comes under OO philosophy. In my opinion, you're mixing business logic with design patterns.
To address your concerns in general, if you have a method that changes the state of the object, which can cause another method to throw an exception based on this new (changed) state, it is desirable to have a state-inspection method so that you don't have to explicitly handle the exception each time.
In your case, let's say you did egg.fry() (on another note, an egg cannot fry itself. So maybe you need a Cook class that takes an egg as an argument to a fry method and returns a FriedEgg instance), and after that you did egg.hatch(). The second call must return an exception. Instead of forcing the user of your class to put that call in an explicit try...catch block, you should provide a state-inspection method; something like isFried() or isHatchable(). Then instead of:
try {
egg.hatch();
}
catch(UnhatchableException e) {
...
}
You have:
if(egg.isHatchable()) {
egg.hatch();
}
So it is the responsibility of the calling code to check and see what state the object is in, before it performs an operation that can potentially throw an exception.
Now assuming you had a Cook class and a method called fry and you did Cook.fry(egg) which returns a FriedEgg instance, what would happen if you call hatch() on it? Common sense would tell you that a fried egg cannot hatch anything!
In this case, you should have an Egg interface with LiveEgg (i.e., an egg that can hatch) and FriedEgg both implementing the Egg interface. The difference however, is in the implementation of the hatch() method in FriedEgg; it would have to throw an UnsupportedOperationException because you cannot hatch a fried egg.
The problem with modeling most real-world scenarios (like cars, animals etc.) is that sometimes they don't help in adequately explaining the relationships. This is due to the fact that OO concepts are pretty abstract.
Anyway, hope this helps.

Doubt in using the interface?

Whenever i hear about interfaces i have the following doubt.
i have the following interface
interface Imammals
{
walk();
eat();
run();
}
and i have two classes Human and Cat that implements this interface.
Anyway, the functionality of the methods are going to be different in both the Classes.
For Eg: walk(), the functionality differs as cat uses four legs and human uses two legs
Then, Why do i need to have a common interface which ONLY declares these methods? Is my design here faulty?
If the functionality of the methods are going to be same in both the classes, i could go for a class based inheritance where the parent implements the complete functionality and the child inherits and uses the parent class methods.
But here the interfaces help us just to consolidate the methods declarations or is there anything more inside?
EDIT: walking(), eating(), running() was changed to walk(), eat(), run() and mammals was changes to Imammals.
In your scenario, either type-inheritance or interface-implementation would work - but interface based abstraction allows types outside of your existing type model to provide the functionality. It could be a mock object, or it could be some kind of super killer robot, that can walk run and eat but isn't really a mammal (so having it inherit from a Mammal class could be confusing or just impossible).
In particular, interfaces allow us to express this relationship neatly, while avoiding the subtle points from C# having single (type-)inheritance.
Using the interface you can have the following:
public void walkMyAnimal(Animal animal) {
animal.walk();
}
without the need to know what animal exactly is passed.
Interface allows you to define behavior for inheriting classes so if you have Donkey in future then you simply implement this interface and be sure that you donkey will walk,run and eat.
Also you can use composition instead of concrete implementation if some of your objects have common behaviour.
Read a bit about Strategy pattern I think that will help.
One big advantage of interfaces is that even in languages like Java and C# where multiple inheritance is not allowed, a class can take on more than one interface. Something can be both Closable, for instance, and a List, but could not inherit from both (hypothetical) abstract base classes AbstractClosable and AbstractList.
It is also suitable for cases where you are writing a library or a plugin interface and want to provide a way for your code to use objects provided by library users or plugin writers, but you don't want (nor should you) any say in the implementation. Think of the Listener interfaces in Java, for instance. Without those, there would be no possibility of an event model, since Java doens't support callbacks.
In general, interfaces are good for cases where you want objects that have particular functionality, but the way that functionality is implemented can vary widely, and might not be the only thing a class does.
The reason you want an interface is to be able to treat them all alike when commanding them.
Whoever calls walking() (which is a rather odd name btw, it should probably be walk()) is just interested in telling your animal to do just that. The actual implementation will vary but that is not something the caller would care about.
Well, sometimes you'd want to be able to do something to "anything capable of running" without necessarily knowing at design time whether you're talking about a human or a cat or whatever. For instance, imagine a function mammal raceWinner(mammal m1, mammal m2){...}
to calculate which mammal would win in a race. To determine who wins, perhaps the function needs to call m1.running() and m2.running(). Of course, the mammals we pass in will really be cats or humans or whatever, and this class supplies the actual implementation of running(). But all raceWinner needs to know is that they have a running() method with the expected signature.
If we only defined running() on cat and human, we couldn't call m1.running() (because the compiler is not guaranteed that m1 has a running() method, as it only knows it's a m1 implements mammal). So instead we'd have to implement a raceWinner(human m1, cat m2) and likewise for two humans, two cats, or any other pair of mammals we had in mind, leading to a lot more work on our part.
An interface provides a contract. It doesn't provide an implementation. It's good practice to interface out your classes.
Of course, walking(), eating() will have different implementation in different animals. But they all walk, run, etc. That is all the interface is saying.
You could model this using inheritance, which would allow you to give default implementations for some or all of the methods. However, interfaces are really useful for declaring a set of features that apply to many unrelated types.
To continue your example, you could imagine a type Alien, which would probably have the same methods, but would not fit in your inheritance hierarchy.
The purpose of interfaces is to tell you what a class does, not how it does it.
This is especially important for accepting things that work differently -- each printer we attach to the PC works differently, so does each scanner, so does each external drive. If all programs needed to care about how each of them worked, you would need to recompile, say, Microsoft Office, for every model of printer that comes out.
One way to develop interfaces is to define an interface and a relative class which implements te interface a common reasonable way. Having both interface and class, you could use the interface in the case the class alreay derives from another class, otherwise a class could derived derivctly to the interface implementation.
It's not always possible, but it solves many problem.
Having a common interface is used to use different object using only the interface (collecting them to a generic list, for example).
There isn't much difference between an entirely abstract class and an interface if you only have one base type. Interfaces can't have any implementation code, but abstract classes can. In this case, abstract classes can be more flexible.
Where interfaces are really useful is that you can assign multiple interfaces to a single implementation, but you can only assign one base class.
for instance, you could have:
class Cat : IMammal, IFourLeggedAnimal
{
}
class Human: IMammal, ITwoLeggedAnimal
{
}
Now you can treat both of them as Mammals, with a "walk()" method, or you can treat them as Four or two legged animals (not necessarily mammals).
What is really useful with an interface like mammal is that you can treat an array of objects (Humans and Cats) as of being of the same type when you want them to walk, eat or run.
For instance if you ware creating a game where you have a number (objects would be created dynamically, but just for example lets say 10 cats and 1 human) of mammals on the screen (saved in a collection), and just wanted them to walk on every turn, you could simply do:
foreach(mammals m in MamalsArrayList){
{
m.walking();
}
note: I suggest you follow naming conventions and name your interfaces with "I" in front of them, so your example should be named IMammals.
without having to know weather any particular m is either a cat or a human.
Interfaces are hard to show on any particular snippet - but when you really need one you can see how useful they can be.
Of course they have other uses to (that are mentioned in other answers), I just focused on your example.
There are two issues here that are often confused. Inherited behaviour allows different 'commands' to be responded to in the same way e.g Man.walk() === Woman.walk(). Polymorphic behaviour allows the same 'command' to be responded to in different ways e.g. Animal.move() for one object may be different for Animal.move() for another, the bird will choose to fly while the slug will slide.
Now, I would argue the second of these is good while the first is not. Why? Because in OOP we should be encapsulating functionality into objects, which in turn promotes code reuse and all the other nicenesses of OOP. So rather than inheriting behaviour we should delegate it out to a shared object. If you know patterns then this is what State and Strategy are doing.
The problem lies in the fact that normally when you inherit , you get both of these behaviours mixed together. I suggest that this is more trouble than its worth and we should only be using interfaces, though sometimes we do have to make do with whatever the framework provides.
In your specific example Mammal is probably a bad interface name because it doesn't really tell me what it does and it has the potential to blowout to thousands of methods. It's better to divide interfaces into very specific cases. If you were modelling animals you might have a Moveable interface with one method, move(), to which each animal could respond by walking, running, flying, or crawling as appropriate.