Do abstract subclasses of an abstract superclass violate LSP? - oop

I'm working with SOLID principles, specifically Liskov's Substitution Principle (LSP).
I have an abstract class called Button that defines different methods and different subclasses extend it to define some of those methods. For example I have a subclass called PauseButton that implement only 3 methods from the abstract class.
Is that concidered a violation of LSP?

The core idea of Liskov's Substitution Principle is that you could use instances of the subtype whenever you may use instances of the type. The subtype may be more specialized but it should comply with the same contract.
In your example, PauseButton fulfills LSP with regard to Button if all its "methods" (we say "operations" in UML) comply with the contract defined for Button. It's not just about implementing the methods but making sure that the methods are compatible:
PauseButton may not strengthen the preconditions of Button. I.e. if the conditions are fulfilled to perform a Button method, you should be able to perform the corresonding PauseButton method as well.
PauseButton may not weaken the postconditions of Button. I.e. if Button makes some promises about a method, PauseButton must fulfill at least the same promises.
Invariants of Button (logical conditions that should be always true) should also be invariants of PauseButton. PauseButton may have additional invariants.
History constraint: roughly speaking, it means that whereas PauseButton is a Button, PauseButton methods should not modify directly its Button state, without using methods of the Button. No shortcut.
In the case of abstract subclasses, it's more delicate to verify LSP since we cannot look at direct instances. Nevertheless, the subclasses could be indirectly instantiated by concrete subclasses that provide the missing methods:
If at least one of the three PauseButton methods that you implement infringes LSP (i.e. are incompatible with the same method from Button), then PauseButton violates LSP.
But if all the three methods are compliant, and if PauseButton does not redefine the contract of the other methods, you can assume that PauseButton is LSP-Compliant : you could create a concrete subclass of PauseButton that would be compliant, even if at the present stage such sublass doesn't exist. This fulfills the requirements for interchangeability.

Related

Subtype vs Subclass: The difference between two

Is it correct to say that in subtyping the interface of the supertype is kept while the observable behaviour is allowed to change, while in subclassing both the interface and the observable behaviour of the superclass must be maintained?

Why Interfaces can not have constructor but abstract classes have constructor

We all know that we can not create the object of both interface and abstract class.But why we are allowed to have constructor in abstract class but not in interface? object of both can not be created.I want very straight forward answer not very complex answer.
Interfaces (at least in .NET and Java, for example) are meant to serve exclusively as what they are named for - to guarantee a certain interface (i.e. set of (public) members) in all implementing classes.
Interfaces are not supposed to have a state, and they are not even supposed to define something like an initial state. Without an initial state, nothing reasonable would be left for a constructor of an interface to be done.
An interface is just a set of member signatures that must be fulfilled by an implementation. Why you can't define constructors? Because constructors are an implementation detail.
Let's say that when you want to hire someone you're not focused on who's the candidate but what can do the candidate (this is a simplification). Does your candidate own desired tech background? Can your candidate do teamwork? From OOP's point of view, you know what a candidate must fulfill to work with you. Therefore, I don't care about a candidate's mother and when/how candidate was born: I care about what properties and behaviors are acceptable for me today. So you would define an interface like CanWorkOnMyCompany for that matter.
In the other hand, an abstract class is a regular class on which some members are signatures and a derived class must provide an implementation to them. That is, you can't instantiate an abstract class because it's not fully implemented.
Probably you could argue that an abstract class can provide no abstract member, but it's not their purpose.
Constructors are for initializing the state of a new instance.
Abstract classes can define state and can have constructors to initialize it.
Interfaces can't define any state and so have no need for constructors.

OOP Principle Differences between Interfaces and Abstract Classes

I understand that Abstract Classes are classes that contain declared methods that do not all necessarily have a specified implementation because the code would have to be declared in the child class instead but Im finding it difficult to understand the OOP concept behind the introduction of Interfaces.
What are the architectural and principle differences between interfaces and abstract classes if the abstract class has no defined methods and states (Aside from the fact that abstract classes can have constructors)?
In addition, why should anyone use abstract classes and interfaces in the first place? I understand that it adds restrictions to your code not allowing people to defined subclasses without specified methods but the code would work in the exact same way if the non implemented declared methods were not present in the interface and abstract class. So what is the implied benefit of writing methods with no implementation only to implement it later in the subclass?
I have seen many posts on Interface vs Abstract Classes but im interested in the principle differences between the two, not their functional differences.
Coming back to my own question after a year, I have discovered the answer that I wanted.
A class, regardless of being abstract or not, always tries to define/design what entities look like from their behaviour to their states. In the case of an abstract class, we are modelling an idea/entity that we do not want to be instantiated during run time. Example, if we had an app about dogs and cats, we may want to define what an animal is and then extend this idea to define what a dog/cat is by extending our base animal class. An animal object will never be instantiated but a dog/cat will.
An interface on the other hand are a set of methods that represents some form of interactions to be expected from any class. As long as a class implements an interface, you know what methods to expect from it. Thus, you can have two entities (classes) that do not relate to one another that implement the same interface. Example, a dog and person class may both implement a 'digest' interface. This means that they are all able to digest food as we have explicitly stated what functions to expect in the interface to enable food digestion behaviour. Obviously the details of the implementation differs thus the functions defined in the interface are outlined in the classes implementing them.

Using Objective-C Introspection vs. Forcing Additional Message Override?

I have a base class which adds some functionality to a number of derived classes in my app.
One of these features is only used by some subclasses.
Currently I'm using a method which returns a BOOL which defaults to NO to "short-circuit" this feature. Subclasses which want the feature must override this method and return YES.
This feature is only useful if you've also overridden at least one of two other methods.
I'd prefer to use class_copyMethodList to determine if the subclass implemented either of these two methods (instead of using the method which returns a BOOL).
What barriers/roadblocks/cons to this approach should I be aware of? Is there a standard implementation of this idiom which I can use?
If I may suggest an alternative approach, have you considered using -instanceMethodForSelector on the relevant subclass instance and comparing to the result on the base class?
That method returns an IMP, which is a C function pointer to the implementation for the given selector. So if the subclass has a different implementation from the base class, it'll return a different IMP.
EDIT: as discussed in the comments below, a further alternative is to declare a formal protocol that the sub classes may implement, and to use NSObject's -conformsToProtocol: to determine whether the protocol is implemented. Since conformsToProtocol returns whether the class has declared support for the protocol (in its #interface via the angle brackets syntax), that's a lot like adding a custom BOOL method that defaults to returning NO but without the syntactic and semantic overhead of adopting your own ad hoc solution.
I have a base class which adds some functionality to a number of derived classes in my app.
This sentence should cause you to rethink your design. A base class should never do anything to derived classes. It should be ignorant of its subclasses. (Class Clusters notwithstanding. That's a separate design approach and require the superclass to be aware in the construction, making it the Factory pattern, which is fine.)
One of these features is only used by some subclasses.
This is a strong indication of a "Square/Rectangle" mistake. In OOP (forget ObjC, this is just CS theory), a square is not a rectangle. You need to ensure that your types conform to Liskov's Substitution Principle. Again, this has nothing to do with any particular language; it's true of all OOP design. It may seem very "theoretical" but it will seriously screw up your implementation if you fail LSP, and you will chase subtle bugs for much longer than you like.
The pattern you probably want here is Decorator rather than subclassing. If you have some special functionality that exists on some classes, you want to encapsulate that functionality into a separate object and attach it to subclasses where it makes sense. Another possible pattern is Strategy (which is generally implemented as a "delegate" in ObjC, which is another way of thinking about Decorator). The point is that you don't want logic in the superclass that is only applicable to some subclasses. You want to put that logic into something that only exists in the appropriate subclasses.
If all of those things fail you, then I strongly recommend a simple (BOOL) function over anything that introspects the method implementations. That way is fragile because it relies on ever-deeper implementation details. respondsToSelector: is definitely better than testing instanceMethodForSelector:.

Liskov substitution principle - no overriding/virtual methods?

My understanding of the Liskov substitution principle is that some property of the base class that is true or some implemented behaviour of the base class, should be true for the derived class as well.
I guess this would mean when a method is defined in a base class, it should never be overrided in the derived class - since then substituting the base class instead of the derived class would give different results. I guess this would also mean, having (non-pure) virtual methods is a bad thing?
I think I might have a wrong understanding of the principle. If I don't, I do not understand why is this principle good practice. Can someone explain this to me? Thanks
Subclasses overriding methods in the base class are totally allowed by the Liskov Substituion Principle.
This might be simplifying it too much, but I remember it as "a subclass should require nothing more and promise nothing less"
If a client is using a superclass ABC with a method something(int i), then the client should be able to substitute any subclass of ABC without problems. Instead of thinking about this in terms of variable types, perhaps think about it in terms of preconditions and postconditions.
If our something() method in the ABC base class above has a relaxed precondition that permits any integer, then all subclasses of ABC must also permit any integer. A subclass GreenABC is not allowed to add an additional precondition to the something() method that requires the parameter to be a positive integer. This would violate the Liskov Substitution Principle (i.e., requiring more). Thus if a client is using subclass BlueABC and passing negative integers to something() the client won't break if we need to switch to GreenABC.
In reverse, if the base ABC class something() method has a postcondition - such as guaranteeing it will never return a value of zero - then all subclasses must also obey that same postcondition or they violate the Liskov Substitution Principle (i.e., promising less).
I hope this helps.
There is one popular example which says if it swims like a duck, quack likes a duck but requires batteries, then it breaks Liskov Substitution Principle.
Put it simply, you have a base Duck class which is being used by someone. Then you add hierarchy by introduction PlasticDuck with same overridden behaviors (like swimming, quacking etc.) as of a Duck but requires batteries to simulate those behaviors. This essentially means that you are introducing an extra pre-condition to the behavior of Sub Class to require batteries to do the same behavior that was earlier done by the Base Duck class without batteries. This might catch the consumer of your Duck class by surprise and might break the functionality built around the expected behavior of Base Duck class.
Here is a good link - http://lassala.net/2010/11/04/a-good-example-of-liskov-substitution-principle/
No, it tells that you should be able to use derived class in the same way as its base. There're many ways you can override a method without breaking this. A simple example, GetHashCode() in C# is in base for ALL classes, and still ALL of them can be used as "object" to calculate the hash code. A classic example of breaking the rule, as far as I remember, is derivin Square from Rectangle, since Square can't have both Width and Height - because setting one would change another and thus it's no more conforms to Rectangle rules. You can, however, still have base Shape with .GetSize() since ALL shapes can do this - and thus any derived shape can be substituted and used as Shape.
Overriding breaks Liskov Substitution Principle if you change any behavior defined by a base method. Which means that:
The weakest precondition for a
child method should be not stronger
than for the base method.
A postcondition for the child method
implies a postcondition for the
parent method. Where a postcondition
is formed by: a) all side
effects caused by a method execution and b)
type and value of a returned expression.
From these two requirements you can imply that any new functionality in a child method that does not affect what is expected from a super method does not violate the principle. These conditions allow you to use a subclass instance where a superclass instance is required.
If these rules are not obeyed a class violates LSP. A classical example is the following hierarchy: class Point(x,y), class ColoredPoint(x,y,color) that extends Point(x,y) and overridden method equals(obj) in ColoredPoint that reflects equality by color. Now if one have an instance of Set<Point> he can assume that two points with the same coordinates are equal in this set. Which is not the case with the overridden method equals and, in general, there is just no way to extend an instantiable class and add an aspect used in equals method without breaking LSP.
Thus every time you break this principle you implicitly introduce a potential bug that reveals when invariant for a parent class that is expected by the code is not satisfied. However, in real world often there is no obvious design solution that does not violate LSP, so one can use, for example, #ViolatesLSP class annotation to warn a client that it is not safe to use class instances in a polymorphic set or in any other kind of cases that rely on the Liskov substitution principle.
I think that you're literally correct in the way you describe the principle and only overriding pure virtual, or abstract methods will ensure that you don't violate it.
However, if you look at the principle from a client's point of view, that is, a method that takes a reference to the base class. If this method cannot tell (and certainly does not attempt to and does not need to find out) the class of any instance that is passed in, then you are also not violating the principle. So it may not matter that you override a base class method (some sorts of decorators might do this, calling the base class method in the process).
If a client seems to need to find out the class of an instance passed in, then you're in for a maintenance nightmare, as you should really just be adding new classes as part of your maintenance effort, not modifying an existing routine. (see also OCP)
The original principle:
"What is wanted here is something like the following substitution property: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.".
Barbara Liskov, 1987
The word is behavior. The "preconditions and postconditions" understanding is useful for a good design but is not related to LSP.
Let's check this summary of "preconditions and postconditions" theory:
Don’t implement any stricter validation rules on input parameters than implemented by the parent class.
Apply at the least the same rules to all output parameters as applied by the parent class.
An indication that it has nothing to do with LSP is: what about VOID methods? VOID does not have OUTPUT parameters. How could this rule be applied to VOID methods? How, according to this rule, could we guarantee to be complying with LSP in VOID methods?
LSP refers to Behavior. When a subclass inherits from a superclass and you have to use some trick to make this work, and the result change the behavior of the program you are breaking LSP.
LSP is about behaviour and the clasic example of Square x Rectangle help us to understand. In fact is the example used by Uncle Bob.
The you inherit Square from Rectangle and overrides SetHeight and SetWidth to force Square act as a Square even if it's a rectangle (by inheritance).
When the user calls SetHeight do not expect Width change.... but will change and this change the expected behavior and break LSP.
This is the problem with Virtuals x LSP