Agile Principles, Patterns, and Practices



Agile Principles, Patterns, and Practices in C#

Written by one of the founders of Agile Development, Robert Martin aka "Uncle Bob" (or Bob as I'll refer to him), its a variation of his book: "Agile Principles, Patterns and Practices" for C#.
I'm reading the C# version since its what's available on Safari Books (free to me!). But not to worry, its not a C# book per se and many of the chapters are language independent. Plus C# is very (very) like Java or even php 5.2+ which is also like Java so its easy to follow the code examples if you know those languages.
Below are mostly key quotes from Bobs book. When I add I'll usually prefix with "Denis aside:"

"
Manifesto for Agile Software Development
We are uncovering better ways of developing
software by doing it and helping others do it.
Through this work we have come to value:
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
That is, while there is value in the items onthe right, we value the items on the left more."

Denis aside: Its still so cool to read it more than 10 years later. Simple, incisive, effective. Changed the industry.


Agile Design

"Agile design is a process, not an event. It’s the continous application of principles, patterns, and practices to improve the structure and readability of the software. It is the dedication to keep the design of the system as simple, clean, and expressive as possible at all times."
 
When doing Agile an initial question is: design? if so, how much?
Bob answers that with: Agile development makes the process of design and architecture continuous. The big picture evolves with the software. The architecture and design is incrementally evolved. But architecture and design are not abandoned.

Bob lists some symptoms of bad design aka "design smells" and he describes object-oriented design principles to eliminate bad design. These principles are each detailed below.

Code "design smells" are often caused by not following the following principles.
But a caution from Bob: don't unnecessarily apply these principles, that could lead to the design smell of needless complexity.

Single Responsibility Principle (SRP)

A class should have only one reason to change.
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.
SRP is derived from the idea of cohesion which is the functional relatedness of the elements in a module.
If a class has more than one responsibility, the responsibilities can become coupled over time. Changes become challenging and the code becomes fragile.

Bob provides as an example a modem class with 4 methods: Dial, Hangup, Send and Recv. Bob says that there are 2 motives for change in this class: 1. connection management (Dial, Hangup) and 2. data communication (Send, Recv).
Bob makes the point that changing the design depends on how the application will change. The modem class may be fine as is if the responsibilities will not change.
(Denis aside: Mind you, that does raise the question in my mind as to how one might know/forsee that? Tough call, in my experience it's better to not take shortcuts in the code design and making a more complete design is better in the long run in my experience. As Bob says later in OCP principle: "experienced designers...judge the probability of various changes"  and make a designs to support)

Bobs solution is to refactor and introduce 2 interfaces which the modem class implements, one for connection management and one for data communication. Thus other classes just use the interfaces, never having to refer to the implementation (from who responsibilities flow away).

Bob provides another example of an Employee class which has methods to CalculatePay() and Store(). A mixing of business rules and persistence. A violation of SRP.
If this design smells of rigidity or fragility then it should be refactored to use Facade, DAO or Proxy patterns.

Denis aside: "Separation of Concerns" (aka Resposibilities) is a key principle of many useful design patterns. Consider MVC, that is a pattern to separate and organize concerns. I see SRP as very much in the same vein.


Open/Closed Principle (OCP)

Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. 
Conforming to OCP means classes/modules are:
1. Open for extension; behavior can be extended; as requirements change we can extend with new behaviors.
2. Closed for modification; extending behavior does not result in changes to class/module 
Code conforms to OCP if it is changed by adding new code rather than by changing existing code.

The common ways to achieve OCP is through abstractions such as (abstract) base classes which are designed to be extended. Change is supported by creating derivatives. Two patterns most commonly used to satisfy OCP are Strategy Pattern and Template Pattern
With Strategy Pattern you define a common interface which concrete classes implement. The appropriate concrete class is instantiated and configured at runtime and becomes a property of a Context and then provided to client who are unaware of concrete classes switched in and out under the covers.
With Template method pattern a base class implements some core behavior with additional abstract/concrete default methods which can be overidden by concrete subclasses for specific concreate behavior. The template method in a parent class controls the overall process by calling subclass methods as required.
Denis aside: I just used a simple variation of Template this week (end Feb). A child view object needed most parent behavior but used a different display template. I created a getTemplateName() method in the base class which the child overrode to just add their name. Pre/post or before/after methods are also examples.
Polymorphism is also a good example of a way to implement OCP.



The Liskov Substitution Principle (LSP)

Subtypes must be substitutable for their base types.

Say you have a method f which takes an object of type A. Then I should be able to pass a derivative/subclass of A and f should this work. Otherwise it violates LSP.
Adding in a check to do specific subtype handling is a code smell.

I think the real gold of this principle is that it helps you arrive at good "is-a" relationships. "is-a" is very important in oo and inheritence e.g. a human is-a mammel.
"Behaviorally, a Square is not a Rectangle, and it is behavior that software is really all about. LSP makes it clear that in OOD, the IS-A relationship pertains to behavior that can be reasonably assumed and that clients depend on."

But if a subtype ends up causing code to violate LSP then its quite possible the inheritance hierarchy is wrong.


The Dependency-Inversion Principle

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend upon details. Details should depend upon abstractions.


High level modules should not depend on low level modules in any way. Changes to low level modules will not impact higher levels. By being independent of low level modules then they are more easily reused.

Traditional designs decompose code into cooperating classes/modules, often higher order (policy level) have references and directly manipulate lower level (detail) classes/modules.
Dependency Inversion inverts that such that higher and lower depend on abstraction and service interfaces are often owned by their clients.

Bob provides a simple example of a code hierarchy:

  Policy Layer  uses -> Mechanism Layer   uses ->  Utility Layer

The Policy layer depends on the Mechanism Layer which in turn depends on Utility Layer, thus changes can ripple up.

A more appropriate Dependency Inversion design would be: each upper layer defines an abstract interface for services it needs (yes the client defines/owns its service interface...mostly) . The lower layers implement these Service Interfaces. Now the lower layers depend on the abstract service interfaces declared in the upper layers. Thus the design now looks more like:

  Policy Layer  uses -> Policy Service Interface  <- implements Mechanism Layer
      Mechanism Layer  uses -> Mechanism Service Interface  <- implements Utility Layer   

Policy Layer is now unaffected by changes to Mechanism Layer and can be supported by an lower level that implements Policy Service Interface

Another way to interpret DIP is "Depend on abstractions" not concrete classes (i.e. only refer, override, extend interfaces not concrete classes.
Denis aside: I wonder how to follow this when using a language such as Javascript which only has concrete objects?

Button and Lamp example: Dependency Inversion can be applied whenever one class sends a message to another. Consider a button which when clicked turns a Lamp on or off.
A basic implementation is Button refers to Lamp and tells it to turn on/off.
But Button now directly depends on Lamp (not good), the Button code refers to Lamp.
The high level policy of button (which is turn off/on) is tied to specific low level details.
BUT if we create a Button Service Layer with turnOff and turnOn methods which Button uses and Lamp implements then now we have inverted the Dependency.
Its also flexible enough that other classes beside Lamp such as Fan or HouseLight or...could also co-operate with Buttons. Plus Bob elaborates on how ButtonServer interface can be made more reusable by changing it to somthing like SwitchableDevice.
Bob provides another example of a Furnace which interacts with a Thermometer and Heater to control a Furnace. Thermometer and Heater are interfaces which are defined in the context of the clients needs and are implemented by lower level devices. Its the interfaces which are passed to the Regulate method. The Dependency is inverted.
Denis aside: note the concrete classes have to be instantiated someplace and then passed into Regulate.

void Regulate(Thermometer t, Heater h,
      double minTemp, double maxTemp)
{
  for(;;)
  {
    while (t.Read() > minTemp)
      wait(1);
    h.Engage();

    while (t.Read() < maxTemp)
      wait(1);
    h.Disengage();
  }
}
  
Denis aside: I think dependency injection (as made popular by Spring) is an implementation enabler of DIP because the dependencies are injected into the clients. Concrete implementation classes can even be created using configuration data, thus minimizing the dependency on concrete classes.
 
"...inversion of dependencies is the hallmark of good object-oriented design. It doesn’t matter what language a program is written in. If its dependencies are inverted, it has an OO design. If its dependencies are not inverted, it has a procedural design. 
The principle of dependency inversion is the fundamental low-level mechanism behind many of the benefits claimed for object-oriented technology. Its proper application is necessary for the creation of reusable frameworks. It is also critically important for the construction of code that is resilient to change. Since abstractions and details are isolated from each other, the code is much easier to maintain."






Denis notes. Bob says "The architecture and design is incrementally evolved. But architecture and design are not abandoned. "
- This is key to understand and is often not easy to achieve. I've experienced projects with large software design phases which became more and more detached from the reality of the system to be built. On the other hand, without some basic architecture and understanding of constraints/goals then you're lost in the "wilderness"without a map. In my practice I strive to create the big picture/architecture, then the detailed design BUT to timebox that work. Things may change but I think you need the longer term architecture&design (launch) as well as short term (each iteration).

Denis aside: Developing alot in Javascript these days I do miss the ability to define Interfaces. Such a powerful abstraction mechanism. To a lesser degree, Javascript typeless nature can allow one too much flexability.


"Principles, patterns, and practices are important, but it’s the people who make them work. As Alistair Cockburn says: “Process and technology are a second-order effect on the outcome of a project. The first-order effect is the people.”
 
"A gelled software team is the most powerful software development force there is."

"Remember, the most volatile things in most software projects are the requirements. The requirements are continuously in a state of flux. This is a fact that we, as developers, must accept! We live in a world of changing requirements, and our job is to make sure that our software can survive those changes. If the design of our software degrades because the requirements have changed, we are not being agile."



"Agile developers are dedicated to keeping the design as appropriate and clean as possible. This is not a haphazard or tentative commitment. Agile developers do not “clean up” the design every few weeks. Rather, they keep the software as clean, simple, and expressive as they possibly can—every day, every hour, and every minute. They never say, “We’ll go back and fix that later.” They never let the rot begin. 
 The attitude that agile developers have toward the design of the software is the same attitude that surgeons have toward sterile procedure. Sterile procedure is what makes surgery possible. Without it, the risk of infection would be far too high to tolerate. Agile developers feel the same way about their designs. The risk of letting even the tiniest bit of rot begin is too high to tolerate."
 
 

Comments

Popular posts from this blog

deep dive into Material UI TextField built by mui

angular js protractor e2e cheatsheet

react-router v6.4+ loaders, actions, forms and more