Larry Steinle

May 25, 2011

Software Principles

Filed under: Guides — Larry Steinle @ 10:15 pm
Tags:

Yesterday’s post, Software Reviews, demonstrated how to measure the quality of a software product based upon business needs. Before we can discuss how to review the software to measure its performance against the specified quality criteria we first must understand what makes a good quality program good in the first place. Today’s post will review foundational software principles that make possible the construction of solid software products.

The Value of Software Principles

To demonstrate the value of software principles I’d like to review how to patch a hole in the wall. First, you begin by cutting around the hole taking care to make a square that extends just above and below the hole and to the center of the joists to each side of the hole. Next you cut a piece of sheetrock that will fit into the hole using drywall screws to secure the sheet to the studs. Once the hole is covered the cracks between the new sheetrock and the original sheetrock must be taped and filled with mud. The mud is used to cover the cracks so that the wall appears seamless to the human eye.

When I first began mudding I naturally followed the line filling it in mud. I would use three coats as recommended but when finished the location of the cracks stood out like a sore thumb. You could see where the mud was applied to the cracks. Even with the lights off you could tell!

I hired a professional to resolve the problem and that’s when I learned what I did wrong. As my new, expensive friend began patching I noticed that after filling the cracks with the first coat, on the second coat he applied a circular motion. He didn’t follow the lines after the first coat. On the third application it was nearly a perfect circle. The mud couldn’t be seen because the circular pattern was much more difficult for the human eye to detect!

Often we write programs in the same way. We follow the crack not realizing that we are making the system fragile. The code works but the patch work can be easily seen. When mudding the wall the way that I applied the mud was just as important as the tools that I used. Likewise, in programming the way that we write software has just as much impact on the solution as the tool that we use.

Software Principles define how to use programming languages to construct a quality software product. The objective of these best practice concepts is to provide guidance on how to design a program so that it is inheritably efficient, flexible, extensible, secure, reliable, repeatable, and verifiable. Use of these best practices results in an application that is easier to transition to new support staff, easier to maintain, has fewer bugs and a greatly reduced cost of ownership over the life of the system.

All code patterns utilize one or more software principles while all code anti-patterns will break one or more principles. While designing and coding software special consideration should be given to these best practices. These concepts should always be used to measure the quality of a program during both the design review and the code review. A program should be immediately refactored when it is discovered that it violates any of these concepts.

I’m in no way implying that a perfect software solution can or should be created. Sometimes a database needs to be denormalized to make it more efficient and usable for real world scenarios. Likewise, sometimes an application’s design needs to be designed in a less than perfect manner to turn it into a feasable solution.

Design Pitfalls to Avoid

Coupling indicates the degree of integration between two entities. Tight coupling implies that a change to one component will have a rippling effect of changes to another component. Low coupling implies that a change to one component will have little effect on another. Highly coupled entities tend to be fragile.

Cohesion measures the degree of purpose in the relationship between two entities. Low cohesion implies that activities performed in a component are unrelated to it’s intended purpose. High cohesion implies that the work is specifically related to the purpose of the component and the entities it is consuming. Low cohesion results in work that is more costly to enhance.

In a nutshell programmers should strive to build programs that are loosely coupled but highly cohesive.

Inheritance vs Interface

Interestingly one of the programmers favorite tools violates the loose coupling, high cohesion guideline. Inheritance automatically infers tight coupling! However, when used correctly inheritance can be highly cohesive. So care must be taken to identify when inheritance truly benefits the system.

I prefer the use of interfaces to establish contracts between components. Interfaces encourage well thought out designs that are more likely to align with the loose coupling, tight cohesion guideline.

After implementing an interface a few times it becomes obvious where code is duplicated and could benefit from inheritance. Interface driven design helps drive out proper use of inheritance. It is after we have proven that inheritance offers value to the design that the code should be refactored.

So begin the design with interfaces. Allow the implementation of those interfaces to define when inheritance truly adds value to the code.

Software Principles

Without further ado here is a list of software principles that I have compiled from wikipedia.org.

Software Principle Description Consequences How To Guidelines
Single Responsibility Principle An entity should have only a single responsibility.
  • Improved readability of the code base.
Each entity (function, property, class, module) should have a single, clearly defined purpose.
Command-query separation The function performs an action or a query but not both. Asking a question should not change the answer.
  • Improved readability of the code base.
  • Reduced risk of exposure to heisenbug errors (errors that occur only when an undetectable state or value is encountered).
  • Produces repeatable results.
  • Easier to verify behavior.
A routine should execute a specific action or get a specific value; never both.
Separation of Concerns (SoC) The analysis of a given problem to identify various tasks required to implement the specified functionality separating each task into its own entity.
  • Improved readability of the code base.
  • Reduced risk of exposure to heisenbug errors.
  • More easily refactorable.
  • Easier to extend.
  • Produces repeatable results.
  • Easier to verify behavior.
  • Each entity has a single, clearly defined purpose.
  • The purpose should be abstracted to increase reusability for other features.
  • Concerns tend to be organized into logical groupings called layers.
  • Each entity has a single, clearly defined purpose.
  • The purpose should be abstracted to increase reusability for other features.
  • Concerns tend to be organized into logical groupings called layers.
Law of Demeter (LoD)Principle of Least Knowledge This principle encourages the design of loosely coupled components.
  • Improved readability of the code base
  • Reduced maintenance costs as errors can be more easily localized.
  • Applications that implement LoD tend to be extensible by design.
  • Each entity should have limited knowledge about other entities.
  • An entity should reference content specifically related to its purpose.
  • Interfaces encouraged as they act as a contract defining how the entities will collaborate without specifying how the logic will be implemented.
  • Each entity should have limited knowledge about other entities.
  • An entity should reference content specifically related to its purpose.
  • Interfaces encouraged as they act as a contract defining how the entities will collaborate without specifying how the logic will be implemented.
Meyer’s Open/Closed principle Entities should be open for extension but closed for modification.
  • Reduces the risk of introducing negative side effects when making seemingly safe changes to a class.
  • Increases coupling.
  • Once defined a class can be changed only to correct an error.
  • Add new functionality with a derived class.
  • Never add new functionality to the completed class.
  • Once defined a class can be changed only to correct an error.
  • Add new functionality with a derived class.
  • Never add new functionality to the completed class.
Polymorphic Open/Closed Principle Entities should be open for extension but closed for modification.
  • Reduces the risk of introducing negative side effects when making seemingly safe changes to a class.
  • Minimizes coupling.
  • Increases cohesion.
  • Provides a safer alternative to duck typed designs.
  • Use of interfaces to define behavior.
  • Implementation is specific to each class that implements the interface.
  • Use of interfaces to define behavior.
  • Implementation is specific to each class that implements the interface.
Design by Contract (DbC) Interfaces should be formal, precise and verifiable.
  • Reduced risk to code injection.
  • Reduced risk to cross-site script attacks.
  • Improved readability
  • Improved consumption as interfaces throw meaningful errors.
  • Improved debugging as errors can be located more quickly.
  • Precondition check: Input must be verifiable and proven to conform to the business rules required by the interface.
  • Postcondition check: Results adhere to expected conditions.
  • Invarients: Ensures that value states that are expected to be static remain unchanged.
  • Precondition check: Input must be verifiable and proven to conform to the business rules required by the interface.
  • Postcondition check: Results adhere to expected conditions.
  • Invarients: Ensures that value states that are expected to be static remain unchanged.
Option-operand Separation Arguments for an entity should contain variables necessary to its function. Options should be managed in an external file.
  • Reduced need to recompile when optional behaviors need to be modified.
  • Increased configurability.
  • Increased usability.
  • Easier to transition to new support staff.
  • Separate options into a data store (file, database, external resource) that can be easily modified outside the compiled code.
  • Example uses: Connection Strings, Environment Variables.
  • Separate options into a data store (file, database, external resource) that can be easily modified outside the compiled code.
  • Example uses: Connection Strings, Environment Variables.
Interface Segregation Principle Interfaces should be as small as possible so that the client isn’t forced to consume behaviors that it doesn’t need.
  • Increased flexible.
  • Easier to extend.
  • Easier to refactor.
  • Low coupling.
  • Use interfaces to clearly define the behavior of a class.
  • Split interfaces when they are establishing a contract that causes the implementer to support multiple purposes.
  • Use interfaces to clearly define the behavior of a class.
  • Split interfaces when they are establishing a contract that causes the implementer to support multiple purposes.
Dependency Inversion Principle
  • Increased flexible.
  • Easier to extend.
  • Easier to refactor.
  • Low coupling.
  • All high-level modules should be based on abstractions.
All high-level modules should be based on abstractions.
Advertisement

2 Comments »

  1. Really like your post, is that ok if I use some of them for some internal development review process?

    Comment by Irene Zhang — September 11, 2012 @ 12:26 pm | Reply

    • Thank you for your comments. Please feel free to use as needed. Happy coding!

      Comment by Larry Steinle — September 11, 2012 @ 12:41 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: