Showing posts with label Notes. Show all posts
Showing posts with label Notes. Show all posts

Monday, February 14, 2022

Notes on LSP from Agile Principles, Practices and Patterns book

I continue sharing my notes on SOLID to prepare the ground for the upcoming The Big Branch Theory Podcast episode about Liskov Substitution Principle.

Ok, so these are the raw notes I took while reading the chapter devoted to Liskov Substitution Principle (LSP) in Robert C. Martin’s Agile Principles, Practices and Patterns in C# book (I added some personal annotations between brackets):

  • “The primary mechanisms behind the OCP are abstraction and polymorphism” <- [but in some languages inheritance is needed to have polymorphism]

  • ”.. questions addressed by the LSP”

    • “What are the desgin rules that govern this particular use of inheritance hierarchies?”

    • “What are the characteristics of the best inheritance hierarchies?”

    • “What are the traps that will cause us to create hierarchies that do not conform to OCP?”

  • LSP -> “Subtypes must be substitutable for their base types”

  • “Violating LSP often results in the use of runtime type checking in a manner that grossly violates OCP”

  • “a violation of LSP is a latent violation of OCP”

  • “… more subtle way of violating LSP” -> “.. use of IS-A relationship is sometimes thought to be one of the fundamental techniques of OOA, a term frequently used but seldom defined […]. However this kind of thinking can lead to some subtle yet significant problems. Generally, these problems are not foreseen until we see them in code”

  • Invariants -> “those properties that must always be true regardless of state”

  • “… when the creation of a derived class causes us to make changes to the base class, it often implies that the design is faulty”

  • “Validity is not intrinsic”

    • “LSP leads us to a very important conclusion: A model, viewed in isolation, cannot be meaningfully validated. The validity of a model can be expressed only in terms of its clients.

    • “When considering whether a particular design is appropriate, one cannot simply view the solution in isolation. One must view it in terms of the reasonable assumptions made by the users of that design”

    • “Therefore, as with all other principles, it is often best to defer all but the most obvious LSP violations until the related fragility has been smelled”

  • “IS-A is about behavior” -> “… it is behavior that software is really all about. LSP makes it clear than in OOD, the IS-A relationship pertains to behavior that can be reasonably assumed and that clients depend on” <- [related to behavioural approach to modelling shown in David West’s Object Thinking, or Rebecca Wirfs-Brock’s Designing Object-Oriented Software]

  • “How do you know what your clients will really expect? There is a technique for making those reasonable assumptions explicit and thereby enforcing LSP…” -> Design By Contract (DBC)

  • “Using DBC, the author of a class explicitly states the contract of that class. The contract informs the author of any client code if the behaviors that can be relied on. The contract is specified by declaring preconditions and postconditions for each method. The preconditions must be true for the method to execute. On completion, the method guarantees that the postconditions are true.”

  • “…the rule for preconditions and postconditions of derivatives, as stated by Meyer, is: ‘A routine redeclaration [in a derivative] may only replace the original precondition by one equal or weaker, and the original postcondition by one equal or stronger’” <- “X is weaker than Y if X does not enforce all the constraints of Y. It does not matter how many new constraints X enforces”

  • “In other words, when using an object through its base class interface, the user knows only the preconditions and postconditions of the base class. Thus, derived objects must not expect such users to obey preconditions that are stronger than those required by the base class. Also, derived classes must conform to all the postconditions of the base. That is, their behaviors and outputs must not violate any of the constraints established for the base class.” “Users of the base class must not be confused by the output of the derived class.” <- [a form of the Least Astonishment Principle]

  • “Contracts can […] be specified by writing unit tests. By thoroughly testing the behavior of a class, the unit tests make the behavior of the class clear. Authors of the client code will want to review the unit tests in order to know what to reasonably assume about the classes they are using”

  • “It’s a big advantage not to have to know or care what kind of [sth] you are using. It means that the programmer can decide which kind of [sth] is needed in each particular instance, and none of the client functions will be affected by that decision”

  • “…the problem with conventions: they have to be continually resold to each developer”

  • “There are occasions when it is more expedient to accept a subtle flaw in polymorphic behavior than to attempt to manipulate the design into complete LSP compliance. Accepting compromise instead of pursuing perfection is an engineering trade-off. A good engineer learns when compromise is more profitable that perfection. However, conformance to LSP should not be surrendered lightly. The guarantee that a subclass will always work where its base classes are used is a powerful way to manage complexity. Once it is forsaken we must consider each subclass individually.

  • “Factoring is a powerfull tool. If qualities can be factored out of two subclassses, there is the distinct possibility that other classes will show up later that need those qualities too”

  • Rebecca Wirfs-Brock, on factoring:”

    • “We can state that if a set of classes all support a common responsibility, they should inherit that responsibility from a common superclass”

    • “If a common superclass does not already exist, create one, and move the common responsibility to it. After all such a class is demonstrably useful […]. Isn’t it conceivable that a later extension of your system might add a new subclass that will support those same responsibilitties in a new way? This new superclass will probably be an abstract class”

  • “Some simple heuristics can give you some clues about LSP violations. These heuristics all have to do with derivative classes that somehow remove functionality from their base class. A derivative that does less that its base is usually not substitutable for that base and therefore violates LSP” <- “The presence of degenerate functions in derivatives is not always indicative of an LSP violation, but it’s worth looking at them when they occur” [see Refused Bequest code smell]

  • “The OCP is at the heart of many of the claims made for OOD. […] The LSP is one of the prime enablers of OCP”

  • “The substitutability of subtypes allows a module, expressed in terms of a base type, to be extensible without modification. That substitutability must be sth that developers can depend on implicitly. Thus, the contract of the base type has to be well and prominently understood, if not explicitly enforced, by the code”

  • “The […] IS-A is too broad to act as a definition of a subtype. The true definition of a subtype is substitutable, where substitutability is defined by either an explicit or implicit contract

This post was also published in Codesai's blog.

Saturday, January 30, 2021

Notes on OCP from Agile Principles, Practices and Patterns book

Some time ago I wrote a post sharing my notes on SRP from Agile Principles, Practices and Patterns book because I was making an effort to get closer to the sources of some object-oriented concepts. I didn’t continue sharing my notes on SOLID because I thought they might not be interesting for our readers. However, seeing the success of the Single responsibility ¿Principle? episode of The Big Branch Theory Podcast for which I used my notes on SRP, I’ve decided to share the rest of my notes on SOLID on Codesai’s blog.

Ok, so these are the raw notes I took while reading the chapter devoted to Open-closed Principle in Robert C. Martin’s Agile Principles, Practices and Patterns in C# book (I added some personal annotations between brackets):

  • OCP -> “Software entities (classes, modules, functions, etc) should be open for extension but closed for modification”

  • “When a single change to a program results in a cascade of changes to dependent modules, the design smells of fragility” <- [No local consequences. See Beck’s Local Consequences principle from Implementation Patterns] “OCP advises us to refactor the system so that further changes of that kind will not cause more modifications. If OCP is applied well, further changes of that kind are achieved by adding new code, not by changing old code that already works”

  • “It’s possible to create abstractions that are fixed and yet represent an unbounded group of possible behaviors”

  • “[A module that uses such abstractions] can be closed for modification, since it depends on an abstraction that is fixed. Yet the behavior of the module can be extended by creating new derivatives of the abstraction”

  • “Abstract classes are more closely associated to their clients than to the classes that implement them” <- [related with Separated Interface from Fowler’s P of EAA]

  • ”[Strategy and Template Method patterns] are the most common ways to satisfy OCP. They represent a clear separation of generic functionality from the detailed implementation of that functionality”

  • Anticipation

    • “[When a] program conforms to OCP. It is changed by adding code rather than by changing existing code”

    • “In general no matter how “closed” a module is, there will always be some kind of change against which it is not closed”

    • “Since closure <- [“closure” here means protection against a given axis of variation or change, see Craig Larman’s Protected Variation: The Importance of Being Closed] can’t be complete, it must be strategic. That is the designer must choose the kinds of changes against which to close the design, must guess at the kinds of changes that are most likely, and then construct abstractions to protect against those changes.”

    • “This is not easy. It amounts to making educated guesses about the likely kinds of changes that the application will suffer over time.” “Also conforming to OCP is expensive. It takes development time and money to create the appropriate abstractions. These abstractions also increase the complexity of the software design”

    • “We want to limit the application of OCP to changes that are likely”

    • “How do we know which changes are likely? We do the appropriate research, we ask the appropriate questions, and we use our experience and common sense.” <- [also requires knowing about the domain. A bit easier to predict in technological boundaries. Listen to the conversation in Single Responsibility ¿Principle?] podcast] “And after all that, we wait until the changes happen!” <- [see Yagni] “We don’t want to load the design with lots of unnecessary abstractions. Rather we want to wait until we need the abstraction and then put them in”

  • “Fool me once”

    • “… we initially write our code expecting it not to change. When a change occurs, we implement the abstractions that protect us from future changes of that kind.” <- [One heuristic: we get to OCP through refactoring to avoid Speculative Generality. Most useful heuristic in unknown territory.]

    • “If we decide to take the first bullet, it is to our advantage to get the bullets flying early and frequently. We want to know what changes are likely before we are very far down the development path. The longer we wait to find out what kind of changes are likely, the more difficult it will be to create the appropriate abstractions.”

    • “Therefore, we need to stimulate changes”

      • “We write tests first” -> “testing is one kind of usage of the system. By writing tests first, we force the system to be testable. Therefore, changes in testability will not surprise us later. We will have built the abstractions that make the system testable. We are likely to find that many of these abstractions will protect us from other kinds of changes later.” <- [incrementally (tests “right after”) might also work]

      • “We use short development cycles”

      • “We develop features before infrastructure and frequently show those feature to stake-holders”

      • “We develop the most important features first”

      • “We release the software early and often”

  • “Closure is based on abstraction”

  • “Using a data-driven approach to achieve closure” <- [OCP is not only an OO principle, see Craig Larman’s Protected Variation: The Importance of Being Closed for more]

    • “If we must close the derivatives […] from knowledge of one another, we can use a table-driven approach”

    • “The only item that is not closed against [the rule that involves] the various derivatives is the table itself. An that table can be placed in its own module, separated from all the other modules, so that changes to it do not affect any of the other modules”

  • “In many ways the OCP is at the heart of OOD.”

  • “Yet conformance to [OCP] is not achieved by using an OOP language. Nor is it a good idea to apply rampant abstraction to every part of the application. Rather it requires a dedication on the part of the developers to apply abstraction only to those parts of the program that exhibit frequent change. <- [applying Beck’s Rate of Change principle from Implementation Patterns]”

  • “Resisting premature abstraction is as important as abstraction itself <- [related to Sandi Metz’s “duplication is far cheaper than the wrong abstraction”]”

For me getting closer to the sources of SOLID principles was a great experience that helped me to remove illusions of knowledge I had developed due to the telephone game effect caused by initially learning about SOLID through blog posts and talks. I hope these notes on OCP might be useful to you as well, and motivate you to read a bit closer to some of the sources.

This post was also published in Codesai's blog.There's also a previous version of this post in this blog.

Wednesday, August 23, 2017

Notes on OCP from Agile Principles, Practices and Patterns book

This post continues with the series of posts publishing my notes about SOLID principles taken from Robert C. Martin's wonderful Agile Principles, Practices and Patterns in C# book.

  • OCP -> "Software entities (classes, modules, functions, etc) should be open for extension but closed for modification <- [Martin's definition. The origin of the principle comes from Bertrand Meyer that gave it a slightly different definition]"
  • "When a single change to a program results in a cascade of changes to dependent modules, the design smells of fragility" <- [related with violating Beck's Local Consequences principle from Implementation Patterns] OCP advises us to refactor the system so that further changes of that kind will not cause more modifications. <- [related with Cockburn's & Larman's Protected Variations] If OCP is applied well, further changes of that kind are achieved by adding new code, not by changing old code that already works"
  • "It's possible to create abstractions that are fixed and yet represent an unbounded group of possible behaviors"
  • "[A module that uses such abstractions] can be closed for modification, since it depends on an abstraction that is fixed. Yet the behavior of the module can be extended by creating new derivatives of the abstraction"
  • "Abstract classes are more closely associated to their clients than to the classes that implement them" <- [related with Separated Interface from Fowler's P of EAA book]
  • "[Strategy and Template Method patterns] are the most common ways to satisfy OCP. They represent a clear separation of generic functionality from the detailed implementation of that functionality"
  • Anticipation
    • "[When a] program conforms to OCP. It is changed by adding code rather than by changing existing code"
    • "In general no matter how "closed" a module is, there will always be some kind of change against which it is not closed"
    • "Since closure can't be complete, it must be strategic. That is the designer must choose the kinds of changes against which to close the design, must guess at the kinds of changes that are most likely, and then construct abstractions to protect against those changes."
    • "This is not easy. It amounts to making educated guesses about the likely kinds of changes that the application will suffer over time."
    • "Also conforming to OCP is expensive. It takes development time and money to create the appropriate abstractions. These abstractions also increase the complexity of the software design"
    • "We want to limit the application of OCP to changes that are likely"
    • "How do we know which changes are likely? We do the appropriate research, we ask the appropriate questions, and we use or experience and common sense. And after all that, we wait until the changes happen!" <- [relates with Yagni, also talking about learning about your domain] "We don't want to load the design with lots of unnecessary abstractions. <- [related with Metz's The Wrong Abstraction] Rather we want to wait until we need the abstraction and then put them in"
  • "Fool me once"
    • "... we initially write our code expecting it not to change. When a change occurs, we implement the abstractions that protect us from future changes of that kind."
    • "If we decide to take the first bullet, it is to our advantage to get the bullets flying early and frequently. We want to know what changes are likely before we are very far down the development path. The longer we wait to find out what kind of changes are likely, the more difficult it will be to create the appropriate abstractions."
    • "Therefore, we need to stimulate changes"
      • "We write test first" -> "testing is one kind of usage of the system. By writing tests first, we force the system to be testable. Therefore, changes in testability will not surprise us later. We will have built the abstractions that make the system testable. We are likely to find that many of these abstractions will protect us from other kinds of changes later <- [related with Feather's The Deep Synergy Between Testability and Good Design]."
      • "We use short development cycles"
      • "We develop features before infrastructure and frequently show those features to stake-holders"
      • "We develop the most important features first"
      • "We release the software early and often"
  • "Closure is based on abstraction"
  • "Using a data-driven approach to achieve closure"
    • "If we must close the derivatives [...] from knowledge of one another, we can use a table-driven approach"
    • "The only item that is not closed against [the rule that involves] the various derivatives is the table itself. An that table can be placed in its own module, separated from all the other modules, so that changes to it do not affect any of the other modules"
  • "In many ways the OCP is at the heart of OOD."
  • "Yet conformance to [OCP] is not achieved by using and OOP language. Nor is it a good idea to apply rampant abstraction to every part of the application. Rather it requires a dedication on the part of the developers to apply abstraction only to those parts of the program that exhibit frequent change."
  • "Resisting premature abstraction is as important as abstraction itself <- [related with Metz's The Wrong Abstraction]"
Other posts in this series:

Tuesday, August 15, 2017

Notes on SRP from Agile Principles, Practices and Patterns book

I think that if you rely only on talks, community events, tweets and posts to learn about a concept, you can sometimes end up with diluted (or even completely wrong) versions of the concept due to broken telephone game effects. For this reason, I think it's important to try instead to get closer to the sources of the concepts you want to learn.

Lately I've been doing some study on object-oriented concepts doing an effort to get closer to the sources. These are the resulting notes on Single Responsibility Principle I've taken from the chapter devoted to it in Robert C. Martin's wonderful Agile Principles, Practices and Patterns in C# book:

  • "This principle was described in the work of [Larry Constantine, Ed Yourdon,] Tom DeMarco and Meilir Page-Jones. They called it cohesion, which they defined as the functional relatedness of the elements of a module" <- [!!!]
  • "... we modify that meaning a bit and relate cohesion to the forces that cause a module, or a class, to change"
  • [SRP definition] -> "A class should have only one reason to change"
  • "Why was important to separate [...] responsibilities [...]? The reason is that each responsibility is an axis of change" <- [related with Mateu Adsuara's complexity dimensions]
  • "If a class has more than one responsibility the responsibilities become coupled" <- [related with Long Method, Large Class, etc.] <- [It also eliminates the possibility of using composition at every level (functions, classes, modules, etc.)] "Changes to one responsibility may impair or inhibit the class ability to meet the others. This kind of coupling leads to fragile designs" <- [For R. C. Martin, fragility is a design smell, a design is fragile when it's easy to break]
  • [Defining what responsibility means]
    • "In the context of the SRP, we define a responsibility to be a reason for change"
    • "If you can think of more than one motive for changing a class, that class has more than one responsibility. This is sometimes difficult to see"
  • "Should [...] responsibilities be separated? That depends on how the application is changing. If the application is not changing in ways that cause the [...] responsibilities to change at different times, there is no need to separate them." <- [applying Beck's Rate of Change principle from Implementation Patterns] "Indeed separating them would smell of needless complexity" <- [Needless Complexity is a design smell for R. C. Martin. It's equivalent to Speculative Generality from Refactoring book]
  • "An axis of change is an axis of change only if the changes occur" <- [relate with Speculative Generality and Yagni] "It's not wise to apply SRP, or any other principle if there's no symptom" <- [I think this applies at class and module level, but it's still worth it to always try to apply SRP at method level, as a responsibility identification and learning process]
  • "There are often reasons, having to do with the details of hardware and the OS [example with a Modem implementing two interfaces DateChannel and Connection], that force us to couple things that we'd rather not couple. However by separating their interfaces, we [...] decouple[..] the concepts as far as the rest of the application is concerned" <- [Great example of using ISP and DIP to hide complexity to the clients] "We may view [Modem] as a kludge, however, note that all dependencies flow away from it." <- [thanks to DIP] "Nobody needs to depend on this class [Modem]. Nobody except main needs to know it exists" <- [main is the entry point where the application is configured using dependency injection] "Thus we've put the ugly bit behind a fence. It's ugliness need not leak out and pollute the rest of the app"
  • "SRP is one of the simplest of the principles but one of the most difficult to get right"
  • "Conjoining responsibilities is something that we do naturally"
  • "Finding and separating those responsibilities is much of what software design is really about. Indeed the rest of the principles we discuss come back to this issue in one way or another"
Agile Principles, Practices and Patterns in C# is a great book that I recommend to read. For me getting closer to the sources of SOLID principles has been a great experience that has helped me to remove illusions of knowledge I had developed due to the telephone game effect of having learned it through blogs and talks.

Other posts in this series:

Saturday, June 15, 2013

Notes to myself: "Kent Beck's Theory of Programming"

Values.
  • Communication.
  • Simplicity.
  • Flexibility.

Principles.
  • Local Consequences.
  • Minimize Repetition.
  • Logic and Data Together.
  • Symmetry.
  • Declarative Expression.
  • Rate of Change.