62

I am trying to understand the SOLID principles of OOP and I've come to the conclusion that LSP and OCP have some similarities (if not to say more).

the open/closed principle states "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".

LSP in simple words states that any instance of Foo can be replaced with any instance of Bar which is derived from Foo and the program will work the same very way.

I'm not a pro OOP programmer, but it seems to me that LSP is only possible if Bar, derived from Foo does not change anything in it but only extends it. That means that in particular program LSP is true only when OCP is true and OCP is true only if LSP is true. That means that they are equal.

Correct me if I'm wrong. I really want to understand these ideas. Great thanks for an answer.

4
  • 5
    This is a very narrow interpretation of both concepts. Open/closed can be maintained yet still violate LSP. The Rectangle/Square or Ellipse/Circle examples are good illustrations. Both adhere to OCP, yet both violate LSP. Commented Dec 6, 2012 at 13:08
  • 1
    The world (or at least the internet) is confused on this. kirkk.com/modularity/2009/12/solid-principles-of-class-design. This guy says the violation of LSP is also violation OCP. And then in the book "Software Engineering Design: Theory and Practice" on page 156 the author gives an example of something which adheres to OCP but violates LSP. I have given up on this.
    – Manoj R
    Commented Dec 6, 2012 at 13:28
  • @JoelEtherton Those pairs only violate LSP if they're mutable. In the immutable case, deriving Square from Rectangle does not violate LSP. (But it's probably still bad design in the immutable case since you can have square Rectangles that are not a Square which does not match mathematics) Commented Dec 6, 2012 at 15:57
  • Simple analogy (from a library writer-user's viewpoint). LSP is like selling a product (library) that claim to implement 100% of what it says (on the interface, or user manual), but actually doesn't (or didn't match what is said). OCP is like selling a product (library) with a promise that it can be upgraded (extended) when new functionality comes out (like firmware), but actually can't be upgraded without a factory service.
    – rwong
    Commented Sep 10, 2013 at 3:33

8 Answers 8

138

Gosh, there are some weird misconceptions on what OCP and LSP and some are due to mismatch of some terminologies and confusing examples. Both principles are only the "same thing" if you implement them the same way. Patterns usually follow the principles in one way or another with few exceptions.

The differences will be explained further down but first let us take a dive into the principles themselves:

Open-Closed Principle (OCP)

According to Uncle Bob:

You should be able to extend a classes behavior, without modifying it.

Note that the word extend in this case doesn't necessarily mean that you should subclass the actual class that needs the new behavior. See how I mentioned at first mismatch of terminology? The keyword extend only means subclassing in Java, but the principles are older than Java.

The original came from Bertrand Meyer in 1988:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Here it is much clearer that the principle is applied to software entities. A bad example would be override the software entity as you're modifying the code completely instead of providing some point of extension. The behavior of the software entity itself should be extensible and a good example of this is implementation of the Strategy-pattern (because it is the easiest to show of the GoF-patterns bunch IMHO):

// Context is closed for modifications. Meaning you are
// not supposed to change the code here.
public class Context {

    // Context is however open for extension through
    // this private field
    private IBehavior behavior;

    // The context calls the behavior in this public 
    // method. If you want to change this you need
    // to implement it in the IBehavior object
    public void doStuff() {
        if (this.behavior != null)
            this.behavior.doStuff();
    }

    // You can dynamically set a new behavior at will
    public void setBehavior(IBehavior behavior) {
        this.behavior = behavior;
    }
}

// The extension point looks like this and can be
// subclassed/implemented
public interface IBehavior {
    public void doStuff();
}

In the example above the Context is locked for further modifications. Most programmers would probably want to subclass the class in order to extend it but here we don't because it assumes it's behavior can be changed through anything that implements the IBehavior interface.

I.e. the context class is closed for modification but open for extension. It actually follows another basic principle because we're putting the behavior with object composition instead of inheritance:

"Favor 'object composition' over 'class inheritance'." (Gang of Four 1995:20)

I'll let the reader read up on that principle as it is outside the scope of this question. To continue with the example, say we have the following implementations of the IBehavior interface:

public class HelloWorldBehavior implements IBehavior {
    public void doStuff() {
        System.println("Hello world!");
    }
}

public class GoodByeBehavior implements IBehavior {
    public void doStuff() {
        System.out.println("Good bye cruel world!");
    }
}

Using this pattern we can modify the behavior of the context at runtime, through the setBehavior method as extension point.

// in your main method
Context c = new Context();

c.setBehavior(new HelloWorldBehavior());
c.doStuff();
// prints out "Hello world!"

c.setBehavior(new GoodByeBehavior());
c.doStuff();
// prints out "Good bye cruel world!"

So whenever you want to extend the "closed" context class, do it by subclassing it's "open" collaborating dependency. This is clearly not the same thing as subclassing the context itself yet it is OCP. LSP makes no mention about this either.

Extending with Mixins Instead of Inheritance

There are other ways to do OCP other than subclassing. One way is to keep your classes open for extension through the use of mixins. This is useful e.g. in languages that are prototype-based rather than class-based. The idea is to amend a dynamic object with more methods or attributes as needed, in other words objects that blends or "mixes in" with other objects.

Here is a javascript example of a mixin that renders a simple HTML template for anchors:

// The mixin, provides a template for anchor HTML elements, i.e. <a>
var LinkMixin = {
    render: function() {
        return '<a href="' + this.link +'">'
            + this.content 
            + '</a>;
    }
}

// Constructor for a youtube link
var YoutubeLink = function(content, youtubeId) {
    this.content = content;
    this.setLink(this.youtubeId);
};
// Methods are added to the prototype
YoutubeLink.prototype = {
    setLink: function(youtubeid) {
        this.link = 'http://www.youtube.com/watch?v=' + youtubeid;
    }
};
// Extend YoutubeLink prototype with the LinkMixin using
// underscore/lodash extend
_.extend(YoutubeLink.protoype, LinkMixin);

// When used:
var ytLink = new YoutubeLink("Cool Movie!", "idOaZpX8lnA");

console.log(ytLink.render());
// will output: 
// <a href="http://www.youtube.com/watch?=vidOaZpX8lnA">Cool Movie!</a>

The idea is to extend the objects dynamically and the advantage of this is that objects may share methods even if they are in completely different domains. In the above case you can easily create other kinds of html anchors by extending your specific implementation with the LinkMixin.

In terms of OCP, the "mixins" are extensions. In the example above the YoutubeLink is our software entity that is closed for modification, but open for extensions through the use of mixins. The object hierarchy is flattened out which makes it impossible to check for types. However this is not really a bad thing, and I'll explain in further down that checking for types is generally a bad idea and breaks the idea with polymorphism.

Note that it is possible to do multiple inheritance with this method as most extend implementations can mix-in multiple objects:

_.extend(MyClass, Mixin1, Mixin2 /* [, ...] */);

The only thing you need to keep in mind is to not collide the names, i.e. mixins happen to define the same name of some attributes or methods as they will be overridden. In my humble experience this is a non-issue and if it does happen it is an indication of flawed design.

Liskov's Substitution Principle (LSP)

Uncle Bob defines it simply by:

Derived classes must be substitutable for their base classes.

This principle is old, in fact Uncle Bob's definition doesn't differentiate the principles as that makes LSP still closely related to OCP by the fact that, in the above Strategy example, the same supertype is used (IBehavior). So lets look at it's original definition by Barbara Liskov and see if we can find out something else about this principle that looks like a mathematical theorem:

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.

Lets shrug on this for a while, notice as it doesn't mention classes at all. In JavaScript you can actually follow LSP even though it is not explicitly class-based. If your program has a list of at least a couple of JavaScript objects that:

  • needs to be computed the same way,
  • have the same behavior, and
  • are otherwise in some way completely different

...then the objects are regarded as having the same "type" and it doesn't really matter for the program. This is essentially polymorphism. In generic sense; you shouldn't need to know the actual subtype if you're using it's interface. OCP does not say anything explicit about this. It also actually pinpoints a design mistake most novice programmers do:

Whenever you're feeling the urge to check the subtype of an object, you're most likely doing it WRONG.

Okay, so it might not be wrong all the time but if you have the urge to do some type checking with instanceof or enums, you might be doing the program a bit more convoluted for yourself than it needs to be. But this is not always the case; quick and dirty hacks to get things working is an okay concession to make in my mind if the solution is small enough, and if you practice merciless refactoring, it may get improved once changes demand it.

There are ways around this "design mistake", depending on the actual problem:

  • The super class is not calling the prerequisites, forcing the caller to do so instead.
  • The super class is missing a generic method that the caller needs.

Both of these are common code design "mistakes". There are a couple of different refactorings you can do, such as pull-up method, or refactor to a pattern such the Visitor pattern.

I actually like the Visitor pattern a lot as it can take care of large if-statement spaghetti and it is simpler to implement than what you'd think on existing code. Say we have the following context:

public class Context {

    public void doStuff(string query) {

        // outcome no. 1
        if (query.Equals("Hello")) {
            System.out.println("Hello world!");
        } 

        // outcome no. 2
        else if (query.Equals("Bye")) {
            System.out.println("Good bye cruel world!");
        }

        // a change request may require another outcome...

    }

}

// usage:
Context c = new Context();

c.doStuff("Hello");
// prints "Hello world"

c.doStuff("Bye");
// prints "Bye"

The outcomes of the if-statement can be translated into their own visitors as each is depending on some decision and some code to run. We can extract these like this:

public interface IVisitor {
    public bool canDo(string query);
    public void doStuff();
}

// outcome 1
public class HelloVisitor implements IVisitor {
    public bool canDo(string query) {
        return query.Equals("Hello");
    }
    public void doStuff() {
         System.out.println("Hello World");
    }
}

// outcome 2
public class ByeVisitor implements IVisitor {
    public bool canDo(string query) {
        return query.Equals("Bye");
    }
    public void doStuff() {
        System.out.println("Good bye cruel world");
    }
}

At this point, if the programmer did not know about the Visitor pattern, he'd instead implement the Context class to check if it is of some certain type. Because the Visitor classes have a boolean canDo method, the implementor can use that method call to determine if it is the right object to do the job. The context class can use all visitors (and add new ones) like this:

public class Context {
    private ArrayList<IVisitor> visitors = new ArrayList<IVisitor>();

    public Context() {
        visitors.add(new HelloVisitor());
        visitors.add(new ByeVisitor());
    }

    // instead of if-statements, go through all visitors
    // and use the canDo method to determine if the 
    // visitor object is the right one to "visit"
    public void doStuff(string query) {
        for(IVisitor visitor : visitors) {
            if (visitor.canDo(query)) {
                visitor.doStuff();
                break;
                // or return... it depends if you have logic 
                // after this foreach loop
            }
        }
    }

    // dynamically adds new visitors
    public void addVisitor(IVisitor visitor) {
        if (visitor != null)
            visitors.add(visitor);
    }
}

Both patterns follow OCP and LSP, however they are both pinpointing different things about them. So how does code look like if it violates one of the principles?

Violating one principle but following the other

There are ways to break one of the principles but still have the other be followed. The examples below seem contrived, for good reason, but I've actually seen these popping up in production code (and even worser):

Follows OCP but not LSP

Lets say we have the given code:

public interface IPerson {}

public class Boss implements IPerson {
    public void doBossStuff() { ... }
}

public class Peon implements IPerson {
    public void doPeonStuff() { ... }
}

public class Context {
    public Collection<IPerson> getPersons() { ... }
}

This piece of code follows the open-closed principle. If we're calling the context's GetPersons method, we'll get a bunch of persons all with their own implementations. That means that IPerson is closed for modification, but open for extension. However things take a dark turn when we have to use it:

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // now we have to check the type... :-P
    if (person instanceof Boss) {
        ((Boss) person).doBossStuff();
    }
    else if (person instanceof Peon) {
        ((Peon) person).doPeonStuff();
    }
}

You have to do type checking and type conversion! Remember how I mentioned above how type checking is a bad thing? Oh no! But fear not, as also mentioned above either do some pull-up refactoring or implement a Visitor pattern. In this case we can simply do a pull up refactoring after adding a general method:

public class Boss implements IPerson {
    // we're adding this general method
    public void doStuff() {
        // that does the call instead
        this.doBossStuff();
    }
    public void doBossStuff() { ... }
}


public interface IPerson {
    // pulled up method from Boss
    public void doStuff();
}

// do the same for Peon

The benefit now is that you don't need to know the exact type anymore, following LSP:

// in some routine that needs to do stuff with 
// a collection of IPerson:
Collection<IPerson> persons = context.getPersons();
for (IPerson person : persons) {
    // yay, no type checking!
    person.doStuff();
}

Follows LSP but not OCP

Lets look at some code that follows LSP but not OCP, it is kind of contrived but bear with me on this one it's very subtle mistake:

public class LiskovBase {
    public void doStuff() {
        System.out.println("My name is Liskov");
    }
}

public class LiskovSub extends LiskovBase {
    public void doStuff() {
        System.out.println("I'm a sub Liskov!");
    }
}

public class Context {
    private LiskovBase base;

    // the good stuff
    public void doLiskovyStuff() {
        base.doStuff();
    }

    public void setBase(LiskovBase base) { this.base = base }
}

The code does LSP because the context can use LiskovBase without knowing the actual type. You'd think this code follows OCP as well but look closely, is the class really closed? What if the doStuff method did more than just print out a line?

The answer if it follows OCP is simply: NO, it isn't because in this object design we're required to override the code completely with something else. This opens up the cut-and-paste can of worms as you have to copy code over from the base class to get things working. The doStuff method sure is open for extension, but it wasn't completely closed for modification.

We can apply the Template method pattern on this. The template method pattern is so common in frameworks that you might have been using it without knowing it (e.g. java swing components, c# forms and components, etc.). Here is that one way to close the doStuff method for modification and making sure it stays closed by marking it with java's final keyword. That keyword prevents anyone from subclassing the class further (in C# you can use sealed to do the same thing).

public class LiskovBase {
    // this is now a template method
    // the code that was duplicated
    public final void doStuff() {
        System.out.println(getStuffString());
    }

    // extension point, the code that "varies"
    // in LiskovBase and it's subclasses
    // called by the template method above
    // we expect it to be virtual and overridden
    public string getStuffString() {
        return "My name is Liskov";
    }
}

public class LiskovSub extends LiskovBase {
    // the extension overridden
    // the actual code that varied
    public string getStuffString() {
        return "I'm sub Liskov!";
    }
}

This example follows OCP and seems silly, which it is, but imagine this scaled up with more code to handle. I keep seeing code deployed in production where subclasses completely override everything and the overridden code is mostly cut-n-pasted between implementations. It works, but as with all code duplication is also a set-up for maintenance nightmares.

Conclusion

I hope this all clears out some questions regarding OCP and LSP and the differences/similarities between them. It is easy to dismiss them as the same but the examples above should show that they aren't.

Do note that, gathering from above sample code:

  • OCP is about locking the working code down but still keep it open somehow with some kind of extension points.

    This is to avoid code duplication by encapsulating the code that changes as with the example of Template Method pattern. It also allows for failing fast as breaking changes are painful (i.e. change one place, break it everywhere else). For the sake of maintenance the concept of encapsulating change is a good thing, because changes always happen.

  • LSP is about letting the user handle different objects that implement a supertype without checking what the actual type they are. This is inherently what polymorphism is about.

    This principle provides an alternative to do type-checking and type-conversion, that can get out of hand as the number of types grow, and can be achieved through pull-up refactoring or applying patterns such as Visitor.

15
  • 9
    This is a good explanation, because it doesn't oversimplify the OCP by implying it always means implementation by inheritance. It's that oversimplification that joins OCP and SRP in some people's minds, when really they can be two completely separate concepts.
    – Eric King
    Commented Dec 6, 2012 at 14:48
  • 10
    This is one of the best stack exchange answers I've ever seen. I wish I could upvote it 10 times. Well done, and thank you for the excellent explanation.
    – Bob Horn
    Commented Dec 8, 2012 at 18:55
  • There, I added a blurb on Javascript that isn't class-based programming language but can still follow LSP and edited the text so it hopefully reads more fluently. Phew!
    – Spoike
    Commented Dec 8, 2012 at 23:43
  • While your quote from Uncle Bob from LSP is correct (same as his website), shouldn't it be the other way around? Shouldn't it state that "Base classes should be substitutable for their derived classes"? On LSP the test of "compatibility" is done against the derived class and not the base one. Still, I am not a native English speaker and I think there may be some details about the phrase that I may be missing.
    – Alpha
    Commented Dec 12, 2012 at 19:42
  • 1
    Amazing, brilliant explanation! Thank you! :-D Commented Jan 27, 2022 at 10:15
15

This is something that causes a lot of confusion. I prefer considering these principles somewhat philosophically, because there are many different examples for them, and sometimes concrete examples don't really capture their whole essence.

What OCP tries to fix

Say we need to add functionality to a given program. The easiest way to go about it, especially for people who were trained to think procedurally, is to add an if clause wherever needed, or something of the like.

The problems with that are

  1. It changes the flow of existing, working code.
  2. It forces a new conditional branching on every case. For example, say you have a list of books, and some of them are on sale, and you want to iterate over all of them and print their price, such that if they're on sale, the printed price will include the string "(ON SALE)".

You can do this by adding an additional field to all books named "is_on_sale", and then you can check that field when printing any book's price, or alternatively, you can instantiate on-sale books from the database using a different type, which prints "(ON SALE)" in the price string (not a perfect design but it delivers the point home).

The problem with the first, procedural solution, is an extra field for each book, and extra redundant complexity in many cases. The second solution only forces logic where it is actually required.

Now consider the fact that there could be plenty of cases where different data and logic is required, and you'll see why keeping OCP in mind while designing your classes, or reacting to changes in requirements, is a good idea.

By now you should get the main idea: Try to put yourself into a situation where new code can be implemented as polymorphic extensions, not procedural modifications.

But never be afraid to analyze the context, and see if the drawbacks happen to outweigh the benefits, because even a principle such as OCP can make a 20-class mess out of a 20-line program, if not treated carefully.

What LSP tries to fix

We all love code reuse. A disease that follows is that many programs don't understand it completely, to the point where they're blindly factoring common lines of code only to create unreadable complexities and redundant tight-coupling between modules that, other than a few lines of code, have nothing in common as far as the conceptual work-to-be-done goes.

The biggest example of this is interface reuse. You've probably witnessed it yourself; a class implements an interface, not because its a logical implementation of it (or an extension in case of concrete base classes), but because the methods it happens to declare at that point have the right signatures as far as it's concerned.

But then you encounter a problem. If classes implement interfaces only by considering the signatures of the methods they declare, then you find yourself able to pass instances of classes from one conceptual functionality into places which demand completely different functionality, that only happen to depend upon similar signatures.

That isn't that horrible, but it causes a lot of confusion, and we have the technology to prevent ourselves from making mistakes like these. What we need to do is to treat interfaces as API + Protocol. The API is apparent in declarations, and the protocol is apparent in existing uses of the interface. If we have 2 conceptual protocols which share the same API, they should be represented as 2 different interfaces. Otherwise we get caught up in DRY dogmatism and, ironically, only create harder to maintain code.

Now you should be able to understand the definition perfectly. LSP says: Don't inherit from a base class and implement functionality in those sub-classes that, other places, which depend on the base class, won't get along with.

1
  • 1
    I signed up just to be able to up vote this and Spoike's answers -- great job.
    – David Culp
    Commented Jun 21, 2015 at 21:34
10

From my understanding:

OCP says: "If you will add new function, create a new class extending an existing one, rather than changing it."

LSP says: "If you create a new class extending an existing class, make sure it's completely interchangeable with its base."

So I think they complement each other but they are not equal.

0
4

While it's true that OCP and LSP both have to do with modification, the kind of modification OCP talks about isn't the one LSP talks about.

Modifying with regard to OCP is the physical action of a developer writing code in an existing class.

LSP deals with the behavior modification a derived class brings compared to its base class, and the runtime alteration of the program's execution that can be caused by using the subclass instead of the superclass.

So although they might look similar from a distance OCP != LSP. In fact I think they may be the only 2 SOLID principles that can't be understood in terms of each other.

2

LSP in simple words states that any instance of Foo can be replaced with any instance of Bar which is derived from Foo without any loss of program functionality.

This is wrong. LSP states that class Bar shouldn't introduce behavior, that is not expected when code uses Foo, when Bar is derived from Foo. It has nothing to do with loss of functionality. You can remove functionality, but only when code using Foo doesn't depend on this functionality.

But in the end, this is usually hard to achieve, because most of the time, code using Foo depends on all of it's behavior. So removing it violates LSP. But simplifying it like this is only part of LSP.

1
  • A very common case is where the substituted object removes side-effects: eg. a dummy logger that outputs nothing, or a mock object used in testing.
    – Useless
    Commented Dec 6, 2012 at 13:50
2

About objects that may violate

To understand the difference you should understand subjects of both principles. It is not some abstract portion of code or situation that may violate or not some principle. It is always some specific component - function, class or a module - that may violate OCP or LSP.

Who may violate LSP

One may check if LSP is broken only when there is an interface with some contract and an implementation of that interface. If the implementation doesn't conform to the interface or, generally speaking, to the contract, then LSP is broken.

Simplest example:

class Container {
    // Should add the object to the container.
    void addObject(object) {
        internalArray.append(object);
    }

    int size() {
        return internalArray.size();
    }
}

class CustomContainer extends Container {
    @Override void addObject(object) {
        System.console.print("Skipping object! Ha-ha!");
    }
}

void fillWithRandomNumbers(Container container) {
    while (container.size() < 42) {
        container.addObject(Randomizer.getNumber())
    }
}

The contract clearly states that addObject should append its argument to the container. And CustomContainer clearly breaks that contract. Thus the CustomContainer.addObject function violates LSP. Thus the CustomContainer class violates LSP. The most important consequence is that CustomContainer cannot be passed to fillWithRandomNumbers(). Container cannot be substituted with CustomContainer.

Keep in mind a very important point. It is not this whole code that breaks LSP, it is specifically CustomContainer.addObject and generally CustomContainer that break LSP. When you state that LSP is violated you should always specify two things:

  • The entity that violates LSP.
  • The contract that is broken by the entity.

That's it. Just a contract and its implementation. A downcast in the code doesn't say anything about LSP violation.

Who may violate OCP

One may check if OCP is violated only when there is a limited data set and a component which handles values from that data set. If the limits of the data set may change over time and that requires changing the source code of the component, then the component violates OCP.

Sounds complex. Let's try a simple example:

enum Platform {
    iOS,
    Android
}

class PlatformDescriber {
    String describe(Platform platform) {
        switch (platform) {
            case iOS: return "iPhone OS, v10.0.1";
            case Android: return "Android, v7.1";
        }
    }
}

The data set is the set of supported platforms. PlatformDescriber is the component that handles values from that data set. Adding a new platform requires updating the source code of PlatformDescriber. Thus the PlatformDescriber class violates OCP.

Another example:

class Shop {
    void sellItemToCustomer(item, customer) {
        // some buisiness logic here
        ...
        logger.logItemSold()
    }
}

class Logger {
    void logItemSold() {
        logger.logToStdErr("an item was sold")
        logger.logToRemote("an item was sold")
        logger.logToDatabase("an item was sold")
    }
}

The "data set" is the set of channels where a log entry should be added. Logger is the component that is responsible for adding entries to all channels. Adding support for another way of logging requires updating the source code of Logger. Thus the Logger class violates OCP.

Note that in both examples the data set is not something semantically fixed. It may change over time. A new platform may emerge. A new channel of logging may emerge. If your component should be updated when that happens, it violates OCP.

Pushing the limits

Now the tricky part. Compare the examples above to the following:

enum GregorianWeekDay {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

String translateToRussian(GregorianWeekDay weekDay) {
    switch (weekDay) {
        case Monday: return "Понедельник";
        case Tuesday: return "Вторник";
        case Wednesday: return "Среда";
        case Thursday: return "Четверг";
        case Friday: return "Пятница";
        case Saturday: return "Суббота";
        case Sunday: return "Воскресенье";
    }
}

You may think translateToRussian violates OCP. But actually it's not. GregorianWeekDay has a specific limit of exactly 7 week days with exact names. And the important thing is that these limits semantically cannot change over time. There will always be 7 days in gregorian week. There will always be Monday, Tuesday, etc. This data set is semantically fixed. It is not possible that translateToRussian's source code will require modifications. Thus OCP is not violated.

Now it should be clear that exhausting switch statement isn't always an indication of broken OCP.

The difference

Now feel the difference:

  • The subject of LSP is "an implementation of interface/contract". If the implementation doesn't conform to the contract then it breaks LSP. It is not important if that implementation may change over time or not, if it's extensible or not.
  • The subject of OCP is "a way of responding to a requirements change". If support for a new type of data requires changing the source code of the component that handles that data, then that component breaks OCP. It is not important if the component breaks its contract or not.

These conditions are completely orthogonal.

Examples

In @Spoike's answer the Violating one principle but following the other part is totally wrong.

In the first example the for-loop part is clearly violating OCP because it is not extensible without modification. But there is no indication of LSP violation. And it is not even clear if the Context contract allows getPersons to return anything except Boss or Peon. Even assuming a contract that allows any IPerson subclass to be returned, there is no class that overrides this post-condition and violates it. More over, if getPersons will return an instance of some third class, the for-loop will do its job without any failure. But that fact has nothing to do with LSP.

Next. In the second example neither LSP, nor OCP is violated. Again, the Context part just has nothing to do with LSP - no defined contract, no subclassing, no breaking overrides. It is not Context who should obey LSP, it is LiskovSub should not break the contract of its base. Regarding OCP, is the class really closed? - yes, it is. No modification is needed to extend it. Obviously the name of the extension point states Do anything you want, no limits. The example is not very useful in real life, but it clearly doesn't violate OCP.

Let's try to make some correct examples with true violation of OCP or LSP.

Follow OCP but not LSP

interface Platform {
    String name();
    String version();
}

class iOS implements Platform {
    @Override String name() { return "iOS"; }
    @Override String version() { return "10.0.1"; }
}

interface PlatformSerializer {
    String toJson(Platform platform);
}

class HumanReadablePlatformSerializer implements PlatformSerializer {
    String toJson(Platform platform) {
        return platform.name() + ", v" + platform.version();
    }
}

Here, HumanReadablePlatformSerializer doesn't require any modifications when a new platform is added. Thus it follows OCP.

But the contract requires that toJson must return a properly formatted JSON. The class doesn't do that. Because of that it cannot be passed to a component that uses PlatformSerializer to format body of a network request. Thus HumanReadablePlatformSerializer violates LSP.

Follow LSP but not OCP

Some modifications to the previous example:

class Android implements Platform {
    @Override String name() { return "Android"; }
    @Override String version() { return "7.1"; }
}
class HumanReadablePlatformSerializer implements PlatformSerializer {
    String toJson(Platform platform) {
        return "{ "
                + "\"name\": \"" + platform.name() + "\","
                + "\"version\": \"" + platform.version() + "\","
                + "\"most-popular\": " + isMostPopular(platform) + ","
                + "}"
    }

    boolean isMostPopular(Platform platform) {
        return (platform instanceof Android)
    }
}

The serializer returns correctly formatted JSON string. So, no LSP violation in here.

But there is a requirement that if the platform is most largely used then there should be corresponding indication in JSON. In this example OCP is violated by HumanReadablePlatformSerializer.isMostPopular function because someday iOS become most popular platform. Formally it means that the set of most used platforms is defined as "Android" for now, and isMostPopular inadequately handles that data set. The data set is not semantically fixed and may freely change over time. HumanReadablePlatformSerializer's source code is required to be updated in case of a change.

You may also notice a violation of Single Responsibility in this example. I intentionally made so to be able to demonstrate both principles on the same subject entity. To fix SRP you may extract the isMostPopular function to some external Helper and add a parameter to PlatformSerializer.toJson. But that's another story.

0
0

LSP and OCP are not the same.

LSP talks about the correctness of the program as it stands. If an instance of a subtype would break program correctness when substituted into the code for ancestor types, then you've demonstrated a violation of LSP. You might have to mock up a test to show this, but you wouldn't have to change the underlying code base. You're validating the program itself to see if it meets LSP.

OCP talks about the correctness of changes in program code, the delta from one source version to another. Behavior should not be modified. It should only be extended. The classic example is field addition. All the existing fields continue to operate as before. The new field just adds functionality. Deleting a field however, is typically a violation of OCP. Here you're validating the program version delta to see if it meets OCP.

So that's the key difference between LSP and OCP. The former validates only the code base as it stands, the latter validates only code base delta from one version to the next. As such they cannot be the same thing, they are defined as validating different things.

I'll give you a more formal proof: To say "LSP implies OCP" would be implying a delta (because OCP requires one other than in the trivial case), yet LSP does not require one. So that is clearly false. Conversely we can disprove "OCP implies LSP" simply by saying OCP is a statement about deltas therefore it says nothing about a statement over a program-in-place. That follows from the fact that you can create ANY delta starting with ANY program in place. They are wholly independent.

-1

I would look at it from client's point of view. if Client is using features of an interface, and internally that feature has been implemented by Class A. Suppose there is a class B which extends class A, then tomorrow if I remove class A from that interface and put class B, then class B should also provide same features to the client. Standard example is a Duck class which swims, and if ToyDuck extends Duck then it should also swim and does not complain that it can't swim, otherwise ToyDuck should not have extended Duck class.

3
  • It would be very constructive if people put comment also while down voting any answer. After all we are all here to share knowledge, and simply passing judgment without proper reason is not going to serve any purpose.
    – AKS
    Commented Aug 2, 2013 at 11:36
  • this doesn't seem to offer anything substantial over points made and explained in prior 6 answers
    – gnat
    Commented May 12, 2014 at 14:12
  • 1
    It sounds like you are just explaining one of the principles, the L one i think. For what It is it's ok but the question asked for a compare/contrast of two different principles. That's probably why someone downvoted it.
    – Weaver
    Commented Sep 23, 2014 at 19:55

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