Principles of software design - Foundation - Microsoft .NET Architecting Applications for the Enterprise, Second Edition (2014)

Microsoft .NET Architecting Applications for the Enterprise, Second Edition (2014)

Part I: Foundation

Chapter 3. Principles of software design

All programming is maintenance programming because you are rarely writing original code. It’s only the first 10 minutes that the code’s original, when you type it in the first time. That’s it.

—Dave Thomas and Andy Hunt

A popular saying in software development is that good architecture is architecture in which all hard-to-change decisions turn out to be right. Software architecture is about implementing behavior around pillars. Changing pillars on the go is obviously problematic, but it might become necessary if the pillars fail to support enough weight, or if you think of a better idea. Ensuring that code can survive changes and extensions is the Holy Grail of software, and it’s exactly the theory that all gurus and book authors propound and recommend you do.

The real world is different.

Detecting alarming symptoms of code deterioration is the easiest part; deciding what action to take to fix them is a bit harder. In general, designing a software system is challenging because it requires you to focus on today’s requested features while ensuring that the resulting system is flexible enough to support any future fixes, changes, and new features.

How would you code to ensure that fixes and extensions can be applied seamlessly and ideally at the sole cost of their implementation?

Maintainability is the quality characteristic you should give the highest priority to when you design a system. Maintainability refers to the degree to which a codebase handles updates without generating regression and new issues. Unfortunately, maintainability is not a one-off feature you can implement; instead, it results from various factors, such as adopting principles and common patterns and paying attention to code cleanliness, readability, and testability.

This chapter provides you with a quick summary of common practices in software engineering. It first outlines some universal principles that should always inspire the design of software and then moves on to discuss principles of object-oriented design and key design vectors such as SOLID.


Image Note

SOLID is now an extremely popular acronym in software development. It’s formed from the initials of five design principles: Single responsibility, Open/close, Liskov’s, Interface segregation, and Dependency inversion. We’ll touch on SOLID principles later in the chapter.


Universal principles of software design

When the two of us started programming, which was long before we started making a living out of it, the old BASIC language was still around with its set of GOTO statements. Like many others, we wrote toy programs, jumping from one instruction to the next within the same monolithic block of code. They worked just fine, but they were only toy programs in the end.


Image Note

Every time we looked at the resulting messy BASIC code we wrote, continually referring to other instructions that appeared a bunch of lines up or down in the code, we didn’t really like it and we weren’t really proud of it. But, at the time, we just thought we were accepting a cool challenge that only a few preordained souls could take on. Programming is a darned hard thing—we thought—but we are going to like it.


It was about the late 1960s when the complexity of the average program crossed the significant threshold that marked the need for a more systematic approach to software development. That signaled the official beginning of software engineering.

From spaghetti code to lasagna code

Made of a messy tangle of jumps and returns, GOTO-based code was soon belittled and infamously labeled as spaghetti code. And we all learned the first of a long list of revolutionary concepts: structured programming. In particular, we learned to use subroutines to break our code into cohesive and more reusable pieces. In food terms, we evolved from spaghetti to lasagna. If you look at Figure 3-1, you will spot the difference quite soon.

Image

FIGURE 3-1 From a messy tangle to a layered and ordered block.

Lasagna forms a layered block of noodles and toppings that can be easily cut into pieces and embodies the concept of structure. Lasagna is also easier to serve, which is the food analogy for reusability.

What software engineering really has been trying to convey since its inception is the need for some design to take place before coding begins and, subsequently, the need for some basic design principles. Overall, we think that the target of software engineering has been moving a bit lately.

Design doesn’t precede coding, but often it goes side by side with coding. As you code, you learn more about the ideal design and refactor to it as soon as possible—ideally, at the beginning of the next iteration. In this way, design and coding evolve together, creating the need for code that can be easily and safely updated without introducing bugs and regression. This is maintainable code—at least, the modern meaning of it.

At the foundation of maintainable code there are two core principles—cohesion and coupling. These principles were first introduced by Larry Constantine and Edward Yourdon in their book Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design(Yourdon Press, 1976).


Image Important

The need for extremely maintainable code also signals a huge shift from methodologies that held the spotlight for many years. Today, a big upfront design is just not affordable and even kind of pointless. Things evolve too fast; companies just need to have their processes improved to catch up with fast-changing business needs. A comprehensive upfront design that is signed-off on at some point and takes ages to be challenged and updated is simply awkward and ill-fitting in today’s dynamic computing world. However, extreme dynamism and agility doesn’t mean you have to dismiss planning, good practices, effective principles, and numbers. It means, instead, that you should stop pointing at the lack of big upfront design as an excuse for poor code.


Cohesion

Cohesion indicates that a given software module—be it a subroutine, class, or library—features a set of responsibilities that are strongly related. Put another way, cohesion measures the distance between the logic expressed by the various methods on a class, the various functions in a library, and the various actions accomplished by a method. If you look for a moment at the definition of cohesion in another field—chemistry—you should be able to see a clearer picture of software cohesion. In chemistry, cohesion is a physical property of a substance that indicates the attraction existing between like-molecules within a body.

Cohesion measurement ranges from low to high and is preferably in the highest range possible.

Highly cohesive classes favor maintenance and reusability because they tend to have no dependencies. Low cohesion, on the other hand, makes it much harder to understand the purpose of a class and creates a natural habitat for rigidity and fragility in the software. Decreasing cohesion leads to creating classes where responsibilities (namely, methods) have very little in common and refer to distinct and unrelated activities. Translated into a practical guideline, the principle of cohesion recommends creating extremely specialized classes with few methods, which refer to logically related operations. If the logical distance between methods grows, you just create a new class.

Ward Cunningham—a pioneer of Extreme Programming—suggests that we define cohesion as being inversely proportional to the number of responsibilities a class has. We definitely like this definition.


Image Important

Strongly related to cohesion is the Single Responsibility Principle (SRP). In the formulation provided by Robert Martin (which you can see at http://www.objectmentor.com/resources/articles/srp.pdf), SRP indicates that each class should always have just one reason to change. In other words, each class should be given a single responsibility, where a responsibility is defined as “a reason to change.” A class with multiple responsibilities has more reasons to change and, subsequently, a less cohesive interface.


Coupling

Coupling measures the level of dependency existing between two software modules, such as classes. Two classes, A and B, are said to be coupled when it turns out that you have to make changes to B every time you make any change to A. In other words, B is not directly and logically involved in the change being made to module A. However, because of the underlying dependency, B is forced to change; otherwise, the code won’t compile any longer.

Coupling measurement ranges from low to high and the lowest possible range is preferable.

Low coupling doesn’t mean your modules are to be completely isolated from one another. They are definitely allowed to communicate, but they should do that through a set of well-defined and stable interfaces. Each module should be able to work without intimate knowledge of another module’s internal implementation. Conversely, high coupling hinders testing and reusing code, and it makes understanding the code nontrivial. It is also one of the primary causes of a rigid and fragile design.

Low coupling and high cohesion are strongly correlated. A system designed to achieve low coupling and high cohesion generally meets the requirements of high readability, maintainability, easy testing, and good reuse.


Image Note

Introduced to support a structured design, cohesion and coupling are basic design principles not specifically related to object orientation. However, it’s the general scope that also makes them valid and effective in an object-oriented scenario. A good object-oriented design, in fact, is characterized by low coupling and high cohesion, which means that self-contained objects (high cohesion) are interacting with other objects through a stable interface (low coupling).


Separation of concerns

A principle that is helpful to achieving high cohesion and low coupling is separation of concerns (SoC), introduced in 1974 by Edsger W. Dijkstra in his paper “On the Role of Scientific Thought.” If you’re interested, you can download the full paper fromhttp://www.cs.utexas.edu/users/EWD/ewd04xx/EWD447.PDF.

Concerns are different pieces of software functionality, like business logic or presentation. SoC is all about breaking the system into distinct and possibly nonoverlapping features. Each feature you want in the system represents a concern and an aspect of the system. Terms such as feature,concern, and aspect are generally considered synonyms. Concerns are mapped to software modules and, to the extent that it is possible, there’s no duplication of functionalities.

SoC suggests you focus on one particular concern at a time. It doesn’t mean, of course, that you ignore all other concerns of the system. More simply, after you assign a concern to a software module, you focus on building that module. From the perspective of that module, any other concerns are irrelevant.


Image Note

If you read Dijkstra’s original text, you’ll see that he uses the expression “separation of concerns” to indicate the general principle but switches to the word “aspect” to indicate individual concerns that relate to a software system. For quite a few years, the word “aspect” didn’t mean anything special to software engineers. Things changed in the late 1990s when aspect-oriented programming (AOP) entered the industry. We love to make the reference here to show Dijkstra’s great farsightedness.


Isolation

SoC is concretely achieved through using modular code and making large use of information hiding. Modular programming encourages the use of separate modules for each significant feature. Modules are given their own public interface to communicate with other modules and can contain internal chunks of information for private use.

Only members in the public interface are visible to other modules. Internal data is either not exposed or it is encapsulated and exposed in a filtered manner. The implementation of the interface contains the behavior of the module, whose details are not known or accessible to other modules.

Isolating parts of the software that are likely to change, or that might require ad hoc settings (for example, security settings), is a common practice that ensures high levels of maintainability.

Object-oriented design

Before object orientation (OO), any program resulted from the interaction of routines. Programming was procedural, meaning that there was a main stream of code determining the various steps to be accomplished.

OO has been a milestone in software design.

OO lets you envision a program as the result of interacting objects, each of which holds its own data and—at least in theory—behavior. How would you design a graph of objects to represent your system? Which principles should inspire this design? We can recognize a set of core principles for object-oriented design (OOD) and a set of more advanced and specific principles that descend from, and further specialize, the core principles.

To find a broadly accepted definition of OOD, we probably need to look at the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) and their landmark book Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994). The book is commonly referred to in literature as GoF, which is the universal acronym for “Gang of Four.”

The gist of OOD is contained in this sentence:

You must find pertinent objects, factor them into classes at the right granularity, define class interfaces and inheritance hierarchies, and establish key relationships among them.

The basics of OOD can then be summarized in the following three points: find pertinent objects, minimize coupling between interfacing objects, and favor code reuse.

In GoF, we also find another excerpt that is particularly significant:

Your design should be specific to the problem at hand but also general enough to address future problems and requirements.

This brings up some more, and subtler, aspects of object-orientation.


Image Important

Object-oriented design is still a topic that all developers should read about, but it’s not strictly related to all aspects of software development. OOD applies mostly to areas where you need modeling. This hardly includes presentation or data access, but it certainly includes the business logic if you choose to represent the entities within the business domain according to the Domain Model pattern. In other parts of the system, you just happen to use classes but you hardly spend time identifying pertinent classes and building sophisticated hierarchies. All you do—that mostly works—is create standalone classes and keep them as thin as possible and with as few responsibilities as possible.


Pertinent classes

Nearly everybody who studied computer science heard at some point that the world is full of objects, whether they are cars, buildings, chairs, or even people. Objects are all around you and, just for this reason, OO is a very natural way to design software systems. You also learned that there’s quite a formal way to associate real objects you see with software objects (for example, classes) you design.

Requirements and use cases offer the raw material that must be worked out and shaped into a hierarchy of pertinent classes. A common practice for finding pertinent objects is tagging all nouns and verbs in the various use cases. Nouns originate classes or properties, whereas verbs indicate methods on classes. Let’s consider the following sample requirement:

To view all orders placed by a customer, the user indicates the customer ID. The program displays an error message if the customer does not exist. If the customer exists, the program displays name, address, date of birth, and all outstanding orders. For each order, the program getsID, date, and all order items.

The sample use case suggests the definition of classes such as User, Customer, Order, and OrderItem. The class Customer will have properties such as Name, Address, and DateOfBirth. Methods on the class Customer might be LoadOrderItems, GetCustomerByID, and ViewOrders.

Note that finding pertinent objects is only the first step. As recommended in the statement that many consider to be the emblem of OOD, you then have to factor pertinent objects into classes and determine the right level of granularity and assign responsibilities.

In particular, the placement of the code for the action of retrieving data is critical. Does this behavior belong to the Customer class or is it part of some additional layer? That mostly depends on the logical context in which the class is being designed, the patterns used to make the design, the level of separation achievable between layers and, why not, the skills and strategic vision of developers and architects.

Program to an interface

Every time you use a class, you establish a deep link between the calling code and the class. You can’t use the calling code if the called class is not available. This form of dependency might or might not be tolerable. It all depends on the nature of the called class and the role it has in the design. Let’s have a look at the following code:

public class SomeComponent
{
public void DoWork()
{
// Get an instance of the logger
Logger logger = new Logger();

// Get data to log
var data = GetData();

// Just log
logger.Log(data);
}
}

The class SomeComponent is tightly coupled with the class Logger and its implementation. The class SomeComponent is broken if Logger is broken or not available and, more importantly, you can’t use another type of logger. If the tightly coupled code belonged to a class that’s logically correlated to the caller, it wouldn’t be that bad to have coupling. The two classes form a logical unit. In other cases, you get a real design benefit if you can separate the interface from the implementation.

The use of the logger in the example is not coincidental.

Logging is one of the canonical examples of a cross-cutting concern. A cross-cutting concern is any functionality you might need to have in a class that’s not strictly related to the requirements set for the class. When you implement a class, you might need things like logging, authentication, threading, caching, object pooling, error handling, localization, and validation. Should these aspects be part of the implementation of the class, or are they better placed outside of the caller class? If set to be part of the class, a cross-cutting concern seriously risks the introduction of code duplication. If referenced as in the previous example, instead, the cross-cutting concern leads to tight dependencies.

A clean decomposition of cross-cutting concerns comes with the use of patterns like Dependency Injection (DI) or Service Locator (SL), where the class is made dependent on an abstraction rather than a truly compiled module. In general, decomposing a cross-cutting concern means programming a class to interfaces rather than implementations.

The Logger class needs to undergo an Extract Interface refactoring aimed at extracting an interface to express the core functionality of the component. Here’s a possible definition for the ILogger interface:

interface ILogger
{
void Log(string data);
}

The refactoring step also produces the following Logger class:

public class Logger : ILogger
{
public void Log(string data)
{
...
}
}

The SomeComponent class now receives—in some way—a reference to an ILogger component. This makes the SomeComponent class able to work with any logger component that implements the interface. The SomeComponent class has been successfully decoupled from the logger, and the resulting code is easier to reuse and extend. For example, logging the behavior of SomeComponent to, say, a database just requires the development of a specific class that implements the interface and the injection of the reference.


Image Important

Strictly related to this example and this principle of OOD are the Dependency Injection and Service Locator patterns. Both patterns provide a way to pass classes programmed to interfaces some actual reference to an interfacing instance.


Composition vs. inheritance

Reusability is a fundamental aspect of the object-oriented paradigm and one of the keys to its success and wide adoption. You create a class one day, and you’re happy with that. Next, on another day, you derive a new class from it, make some changes here and there, and come up with a slightly different version of the original class.

Is this what code reuse is all about? Well, there’s more to consider.

With class inheritance, the derived class doesn’t simply inherit the code of the parent class. It really inherits the context and, subsequently, it gains some visibility of the parent’s state. Is this a problem?

For one thing, a derived class that uses the context it inherits from the parent can be broken by future changes to the parent class. In addition, when you inherit from a class, you enter into a polymorphic context, meaning that your derived class can be used in any scenarios where the parent is accepted. It’s not guaranteed, however, that the two classes can really be used interchangeably. Ensuring that this is what happens is your responsibility as a good object-oriented developer; it doesn’t come for free out of the implementation of OO languages. Providing the guarantee that the parent and its derived classes can be used interchangeably is the goal of Liskov’s principle, which we’ll discuss later.

The inherent limitation of inheritance—one of the popular pillars of OO—was recognized quite soon. In fact, in GoF the authors identify two routes to reusability: white-box and black-box reusability. The former is based on class inheritance and suffers from the issues we just mentioned. The latter is based on object composition.

Object composition entails creating a new type that holds an instance of the base type and typically references it through a private member:

public RegisteredUser
{
private User theUser;

public RegisteredUser()
{
// You can use any lazy-loading policy you want for instantiation.
// No lazy loading is being used here ...
theUser = new User();
}

public object DoWork()
{
var data = theUser.DoSomeWork();

// Do some other work
return Process(data);
}

private object Process(object data)
{
...
}
}

In this case, you have a wrapper class that uses a type as a black box and does so through a well-defined contract. The wrapper class—RegisteredUser—has no access to internal members of User and cannot change the behavior in any way. It uses the object as it is rather than changing it to do its will. External calls reach the wrapper class, and the wrapper class delegates the call internally to the held instance of the class it enhances. (See Figure 3-2.)

Image

FIGURE 3-2 Object composition in action.

The logic that ties together the wrapper and wrapped class is up to you. You decide which part of the User class should be exposed outside and how. Figure 3-3, instead, shows how the diagram should be rendered in the case of inheritance.

Image

FIGURE 3-3 Relationship between a base class (User) and a derived class (RegisteredUser).

Composition has one key benefit you don’t get with inheritance: it is a form of defensive programming and, as such, it makes it harder to introduce subtle bugs related to object-oriented known bad practices, such as fragile base classes, virtual members, and constructors.

Two classes composed together have no explicit relationships; you can’t use the RegisteredUser class where an instance of User is expected. If this turns out to be a problem, you can have both classes implement some common IUser interface.


Image Important

Another point in favor of composition that we like to point out is that it explicitly denies inheritance. In this way, it helps developers—especially junior developers—not to think of objects as a new way to design the world in its entirety. When it comes to object-oriented design, it is key that the model is tailor-made and optimized for the specific problem. Sometimes, instead, we tend to abstract too much, and this can produce a less-than-optimal graph of classes.


A second pass at object-orientation

We believe that nearly everybody would agree that OO is great in theory but that, in practice, it needs some adjustment. And the amount of adjustment might sometimes be quite large and fully wipe out the benefits. OO was originally perceived and pushed as a way to tackle complexity. Because objects are all around us, if only we could model software entities after real-world objects we’d be mostly done. Modeling proved quite hard, though. The net result is that we too often end up adding artificial complexity instead of taming the natural complexity of things.


Image Terminology

The term anemic domain refers to a hierarchy of classes that just contain properties and nearly no behavior and internal logic. A rich model, called a domain model in DDD, is a model where classes for the most part are made of data (properties) and behavior (methods).


Sometimes some artificial complexity is created by the wrong perspective on things. In general, when you study object-orientation at school, teachers mostly offer an academic perspective of things where the emphasis is on aspects like abstraction, technology neutrality, elegance of design, or even individual creativity. When students become professionals and are called upon to write real-world code in the industry, they are instead forced to focus on other parameters, such as testability, reusability, effectiveness, and especially maintainability.

Inheritance, in particular, is sometimes more pain than gain as the GoF’s argument against composition proves. There are safe ways to use inheritance—just adding code to derived classes—but the resulting graph of objects can hardly be the faithful representation of a whole model from some domain in the real world. The effectiveness of OO for the most part has been demonstrated in labs or toy or fortunate projects. Most projects experienced trouble, and it’s not really relevant whether such failures were the result of the inherent shortcomings of OO or limited skills. Either way, OO can hardly be seen as the universal paradigm of software development.

In the name of code reuse, OO might sneakily slip gratuitous complexity and subtle bugs into the mix. Experts can probably manage it, but will you have only experts on your team? To really write effective code, we believe that developers should focus on the following three points:

• Isolate parts that likely change.

• Sometimes classes are not required; if all you need is a function, just use a function.

• Be aware that the real world offers events rather than models, and events just carry data.

For some reason, OO represents the mainstream paradigm today because most popular languages are in one way or another object-oriented. Languages—even object-oriented languages—are just tools for expressing a behavior in the most reasonable and maintainable way. In this regard, here are a few remarkable words about OO from Edsger Dijkstra: “Object-oriented programming is an exceptionally bad idea which could only have originated in California.”


Image Note

We like to point you to the following post, which ironically summarizes many of the arguable points about object-orientation: http://hadihariri.com/2013/11/24/refactoring-to-functionalwhy-class.


Development and design vectors

Universal principles such as coupling, cohesion, and SoC, along with OOD principles, give us some rather general guidance about how to design a software application. As you might have noticed, all these principles are rather old (but certainly not outdated), as they were devised and formulated at least a couple of decades ago.

In more recent years, some of these principles have been further refined and enhanced to address more specific aspects of the design.

SOLID principles

A ways back, Robert C. Martin identified five fundamental principles for clean and more effective software design. These principles later were shortened to the popular SOLID acronym. The acronym originates from the initials of the names of the principles:

• Single Responsibility (SRP)

• Open/Closed (OCP)

• Liskov’s principle (LSP)

• Interface segregation (ISP)

• Dependency inversion (DIP)

Object modeling is neither easy nor is it an exact science. Principles exist for the most part to show you the way to go—to give guidance and possibly to point you in the right direction. The whole point of modeling is finding the right mix of principles. Some of them might even be in conflict. Some SOLID principles (in particular, SRP and OCP) might really be hard to apply extensively in a large codebase. They might lead you to continuous refactoring until you discover that over a certain threshold the curve of effort grows while the curve of benefit starts decreasing. Blind focus on principles—in particular, SRP and OCP—might distract you from the main target, namely producing maintainable code that works.


Image Note

You might want to listen to a podcast by Joel Spolski on the theme of testability, quality of code, and SOLID. Find it here: http://www.joelonsoftware.com/items/2009/01/31.html.


Single Responsibility principle

When a class grows big, featuring methods upon methods, there is a chance it is exposing way too many responsibilities. And what’s the ideal number of responsibilities for a class? Just one. And what’s the definition of a responsibility? A responsibility is a reason to change. The Single Responsibility principle says

There should never be more than one reason for a class to change.

In the end, the whole point of this principle is that each class should be ideally built around one core task. Rather than simplicity in itself, the main point here is that by exposing a very limited number of responsibilities, the class intersects a smaller segment of the system and as requirements change the likelihood that you need to edit the class is lower.

Too often SRP is used as a blind measure of quality for code. By taking SRP to the limit, though, you seriously risk producing a wealth of anemic classes—just properties and little or no behavior. Every class tends to take the smallest possible amount of responsibility, but you still need places to concentrate all the orchestration logic that the system still requires. The resulting model might be less balanced than ideal.

That’s why we call SRP a vector—when writing code, keep in mind that classes should be as simple as possible and focus on one main core task. But that shouldn’t become religious dogma or a measure of performance and quality.

Open/Closed principle

The Open/Closed principle invites developers to create software entities (whether classes, modules, or functions) that can happily survive changes. The principle says the following:

A module should be open for extension but closed for modification.

Open for extension ultimately means that an existing class should be extensible and usable as the foundation for building other related functionality. But to implement other related functionality, you should not change the existing code which, subsequently, remains closed for modification.

OCP encourages the use of programming mechanisms like composition, interfaces, and generics that can generate a class that can be extended without modifying the source code. For example, if you program a class to log its activity using a generic logging interface, you automatically enable that class to use any logger that exposes the interface. If you use composition, you can build up new functionality on top of existing components without touching them.

Liskov’s principle

As discussed earlier, inheritance can be a tricky thing, and composition is definitely a safer way to code classes. In particular, in a derived class you can do things that actually prevent the derived class from being used in any place where the parent class is accepted. This subtle aspect of object-orientation is often the source of bad surprises for developers. For this reason, we suggest you keep it in mind whenever you happen to use inheritance in your codebase. The principle says the following:

Subclasses should be substitutable for their base classes.

Apparently, you get this free of charge from just using an object-oriented language. The reality is different. The essence of the Liskov’s principle is that a derived class can’t restrict conditions under which a base class executes. Likewise, a derived class can’t avoid producing some of the results the parent class guarantees. This fact is often summarized by saying that a derived class should require no more and provide no less than its parent.

The canonical example of the Liskov’s principle is the square/rectangle hierarchy.

At first sight, deriving Square from Rectangle seems like a wise and even elegant idea. Isn’t a square just a special type of rectangle? Well, we recommend you consider the expression “special type of” used in the context of class inheritance to be an alarm bell for possible violations of the Liskov principle. If it sounds like it is a special type of some other entity, use inheritance very carefully!

An API designed to receive and handle Rectangle objects might fail when it receives a Square object that, say, forces height and weight to be equal. In other words, the preconditions set on a Rectangle should not be restricted by any derived class with the addition of a new precondition.


Liskov’s principle and code-assistant tools

Code-assistant tools help a lot in catching nasty bugs during editing. If you’re responsive to suggestions, you can really fix your code quickly and in real time.

In past chapters, we referred to “writing code right the first time” as the most effective way to increase the quality of code in software projects. Augmenting skills is certainly one way of getting to that, but it takes time and a lot of good will, and it is costly too. Using a well-made code-assistant tool costs you much less and makes basic refactoring one or two clicks away. These tools (for example, ReSharper) also let you do a lot more sophisticated things but—trust us—just the bare minimum is good enough.

Another tool to pay attention to is Code Contracts. Introduced with the Microsoft .NET Framework 4.0, Code Contracts is an API you can use to specify preconditions and postconditions on class methods. Interestingly, when you have a derived class that overrides a virtual method and adds a precondition, you get a warning at compile time, as shown in Figure 3-4.

Image

FIGURE 3-4 The Code Contracts tools can catch violations of Liskov’s principle at compile time.

The warning message you get from Visual Studio is a bit obscure, but at least it states clearly that the problem is the extra Requires precondition. Let’s consider the following code:

public class User
{
public virtual void DoSomething(int number)
{
return;
}
}
public class RegisteredUser : User
{
public override void DoSomething(int number)
{
Contract.Requires<ArgumentException>(number > 0);
}
}

It’s easy to see that RegisteredUser puts an additional constraint on the method DoSomething and doesn’t provide the same set of services as User. An API built around User might be used to call the method DoSomething, passing a negative value. In the moment in which the API is passed a RegisteredUser instance, the code is broken.


Sometimes violations of Liskov’s principle generates nasty runtime errors, and the quickest way to fix them is by checking the actual type of the parameter being passed. Here’s an example:

public class MyApi
{
public void DoSomething(Rectangle rect)
{
if (typeof(rect) is Square)
return; // or whatever is best here

// Regular code here
...
}
}

This is a clear symptom that something is wrong with your code.

Another common code issue that might be traced back to Liskov’s principle is the use of virtual methods. Whenever you define a virtual method, you should make sure you’re not calling any private member from within it. Doing so would restrict implementors of derived classes from overriding the method having access to the same share of the context of the parent class.

Note that Liskov’s principle also applies to interfaces rather than just to derived classes.

Interface Segregation principle

The Interface Segregation principle addresses interface obesity and recommends that interfaces be kept to a bare minimum of functions. The primary point is not that you should have thin interfaces, but that you should not have fat interfaces. The principle says this:

Clients should not be forced to depend upon interfaces that they do not use.

Correct application of the principle means factoring out interfaces into multiple sets of functions so that it is easier for each client to match just the set of function it is really interested in. In addition, unlike classes, interfaces can be summed up in a class declaration. So there’s really no reason for having a single interface with 20 methods instead of two or three distinct and smaller interfaces. Segregation, though, makes sense only if splitting has some functional sense.

The canonical example of segregated interfaces starts with the following interface:

public interface IDoor
{
void Lock();
void Unlock();
Boolean IsDoorOpen { get; }

Int32 OpenTimeout { get; set; }
event EventHandler DoorOpenForTooLong;
}

The IDoor interface can be simplified and split in two functional groups: door and timed door.

public interface IDoor
{
void Lock();
void Unlock();
Boolean IsDoorOpen { get; }
}
public interface ITimedDoor
{
Int32 OpenTimeout { get; set; }
event EventHandler DoorOpenForTooLong;
}

Failing to adequately adhere to the Interface Segregation principle leads to implementations that are complex and to having many methods that aren’t implemented at all. Furthermore, clients are forced to depend on interfaces they don’t use—and still, these clients are subject to changes to such interfaces.

Dependency Inversion principle

DIP is an extremely important principle, and it is the theoretical foundation for common patterns such as Dependency Injection and Service Locator, which we’ll address in a moment. DIP says the following:

High-level modules should not depend upon low-level modules. Both should depend upon abstractions.

Dependency inversion is a formal way to express the concept behind the “programming to interfaces, not implementations” principle. When you are writing a method and feel the need to call into an external component, as a developer you should wonder whether the function you’re going to call is private to the class or is an external dependency. If it is an external dependency, you just abstract it to an interface and proceed coding against the interface. Next, you’re left with the problem of how to turn the interface into some concretely callable instances. That’s the realm of patterns such as Dependency Injection.

Patterns for handling dependencies

The idea behind DIP doesn’t really need a complex scenario to be effectively illustrated. Just imagine a method that reads bytes from a stream and writes them out to some buffer:

void Copy()
{
Byte byte;
while(byte = ReadFromStream())
WriteToBuffer(byte);
}

The pseudocode here depends on a couple of lower level modules: the reader and writer. According to DIP, we should abstract the dependencies to interfaces—say, IReader and IWriter. Subsequently, the method can be rewritten as follows:

void Copy()
{
Byte byte;
IReader reader;
IWriter writer;

// Still need to instantiate reader and writer variables
...

while(byte = reader.Read())
writer.Write(byte);
}

Who really does provide instances of the reader and writer components? To actually solve the issue, you need some further specification. In other words, you need a pattern.

Service Locator pattern

The first pattern used to apply the DIP is the Service Locator pattern, which can be summarized as follows:

void Copy()
{
Byte byte;
var reader = ServiceLocator.GetService<IReader>();
var writer = ServiceLocator.GetService<IWriter>();

while(byte = reader.Read())
writer.Write(byte);
}

You use a centralized component that locates and returns an instance to use whenever the specified abstraction is requested. The service locator operates while embedded in the code that it serves. It acts as a factory and can include discovery and activation logic as complex as you need. It is common to create a service locator as a class that exposes a few static factory methods, as shown here:

public class ServiceLocator
{
public Object GetService(Type typeToResolve) { ... }
public T GetService<T>() { ... }
public Object GetService(String typeNickname) { ... }
}

You can indicate the type you want to be resolved as a parameter or through a generic prototype. If the same type can be resolved in different ways depending on the context, it is common to pass a plain string nickname for the type to be instantiated. Internally, the GetService method can be written along the following guidelines:

public Object GetService(String typeNickname)
{
if (typeNickname == "sometype")
return new SomeType();
if (typeNickname == "someothertype")
return new SomeOtherType();
...

}

Obviously, the service locator can employ sophisticated forms of instantiation (indirect creation, object pooling, singleton) and read abstract/concrete type mappings from some configuration file.

With regard to the downside of this pattern, we like to mention that it requires you to deeply look into the code to figure out how dependencies are handled. On the other hand, the most compelling scenario for it is when you have to add extensibility to some large legacy code that is hard to redesign in a different way. An excellent example of the Service Locator pattern is the way the ASP.NET MVC team managed to add new extensibility points in the codebase in the transition to version 3 of the framework when they introduced the DependencyResolver class.


Image Important

Service Locator is considered, in many cases, an anti-pattern. The reason is that you end up having references to the service locator class spread out in your code. Worse yet, you won’t get errors until execution time. The following is a good post to read to understand why Service Locator might be considered an anti-pattern: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern. The comments are also pretty good to read.


Dependency Injection pattern

A better alternative is to use the Dependency Injection pattern. (It’s also known as the Inversion of Control, or IOC, pattern.) The resulting code looks like this:

void Copy(IReader reader, IWriter writer)
{
Byte byte;
while(byte = reader.Read())
writer.Write(byte);
}

The list of dependencies is now explicit from the signature of the method and doesn’t require you to go down the line to pinpoint calls to a service locator component. In addition, the burden of creating instances for each spot dependency is moved elsewhere.

There are three ways to inject dependencies into a class: using the constructor, a setter property, or an interface. All three techniques are valid, and the choice is up to you. We usually opt for injecting through the constructor because in this way you make it clear from the beginning what the dependencies of a class are.

Dependency injection based on constructors is also a great way to detect if a class “smells.” If you spot a class with, say, 20 dependencies in the constructor, chances are good that the class is not taking SRP literally!

The key point of Dependency Injection is that factory code resides outside the class. This code, however, can be repetitive and long enough in nontrivial scenarios. It can be so boring and error-prone that it largely waters down the benefits of the pattern. For this reason, Dependency Injection is often associated with specific productivity tools, known as IoC containers.

An IoC container is built around a container object that, bound to some configuration information, resolves dependencies. The caller code instantiates the container and passes the desired interface as an argument. In response, the IoC framework returns a concrete object that implements that interface. Applying Dependency Injection right probably requires that you call your IoC container directly only once, and all it takes is a few lines of code because most of the dependencies usually will be handled by constructors of involved classes.

Microsoft currently provides several IoC choices. One is Managed Extensibility Framework (MEF), which primarily targets extensibility and offers great plugin support in client applications. Next, there are MEF 2 and Unity, which you can discover at http://unity.codeplex.com. MEF 2 is available as a NuGet package for .NET 4.5 and Windows Store applications.

Unity and MEF 2 can be considered full IoC containers. MEF, on the other hand, covers only the basic functions of an IoC container.


Image Note

As we see things, Microsoft .NET trends in the server and on the cloud side seem to focus more strongly on Dependency Injection patterns while promoting it as a more fundamental topic. This emphasis by Microsoft will make SOLID principles and DI even more important to be understood and used by architects and developers. Dependency injection nowadays is a fundamental concept.



Image Important

Dependency Injection and Service Locator are also fundamental patterns from a testability standpoint, because these patterns make it natural to inject dependencies into classes as you test them.


Coding vectors

There’s another class of software principles that you should definitely be aware of. DRY, KISS, YAGNI and a few others just belong to that category. We like to call principles in this class coding vectors.

So what’s the difference between vectors and principles?

In physics, a vector is a quantity fully described by magnitude and direction. So compared to a plain scalar quantity, a vector adds up to a new level of information—the direction.

Applied to software, we tend to use the term vector to point to principles that express both magnitude and direction. In this case, we also consider direction to be predominant to magnitude. The set of principles being described here should be considered for the direction they point at rather than for a concrete pattern to bring into your codebase. These principles apply to the software project as a whole and are not limited to classes and algorithms.

KISS

KISS stands for Keep It Simple, Stupid and points at having systems, including software systems, where any level of logic found in the implementation is strictly necessary. The principle considers unneeded complexity to be any layer of logic that can be considered extra given the system requirements. The principle also balks at the Occam’s razor principle that suggests the hypothesis with the fewest assumptions should always be chosen.

Is there anybody out there who believes that simplicity is not a virtue? In life, simplicity sometimes has a negative connotation, such as something that is not sophisticated and rich enough in terms of features. In software, too often developers tend to over-engineer things; so KISS is a word to the wise to remind you that bottom-up building with a full perspective on things is the ideal way to create software.

We particularly love to mention a couple of corollaries to the KISS theorem. One is from Antoine de Saint-Exupéry, French poet and pioneer aviator. He used to say that perfection is achieved not when there is nothing left to add, but when there is nothing left to take away. The other quote is from Albert Einstein who was often recommended keeping things simple—specifically, as simple as possible, but no simpler. Because—we add—simpler than simple is often simplistic. And simplistic software is definitely a bad thing.

We often like to summarize the “simplicity above all” concept by paraphrasing people’s rights in court: everything you write can and will be used against you in a debugging session.

And, worse yet, it will be used in every meeting with the customer.

YAGNI

YAGNI is the acronym of You Ain’t Gonna Need It, which is a popular principle from Extreme Programming. Related to KISS, the principle blames problems on the implementation of any functionality that is not strictly dictated by requirements. Implementing a function when you foresee you might need it, rather than when you actually need it, exposes you to a number of potential headaches. First and foremost, it means more coding, debugging, and testing. It might also mean more documentation for the rest of the team. Furthermore, if the feature is unnecessary today, just the simple fact it exists could constrain future extensions. Finally, if the feature is not expressly required at a given time, are you sure you know enough about it?

YAGNI, though, is a vector; it has an inherent value (namely, magnitude) and gives you guidance (namely, it shows you a direction). If you blindly apply YAGNI to your project, you might fail to plan features ahead of time. When a relevant feature has to be implemented at the latest possible moment, it might require a lot more work than if it were planned in advance.

YAGNI is not a principle that must be treated like a doctor’s prescription. It is a general guideline, and it points you in the right direction.

DRY

DRY is the acronym of Don’t Repeat Yourself. It is a principle formulated by Andy Hunt and Dave Thomas in the book The Pragmatic Programmer: From Journeyman to Master (Addison-Wesley, 1999). Too hastily interpreted as a plain—and fairly obvious, indeed—recommendation to avoid code duplication, DRY in the authors’ minds addresses a far grander point. Every aspect of the software development process, and every deliverable, should have just one credible and unambiguous representation.

DRY certainly applies to code duplication, but it also touches on data models, services, storage, tests, and documentation. More than everything else, though, DRY attempts to teach a method. You should avoid the trouble of keeping different parts of the system in sync. If you need to make a change anywhere, you should be able to do it once, and that should be enough.

When just applied to code, DRY becomes a sort of once-and-only-once kind of recommendation. You write the code that accomplishes a given operation within an application only once. Put this way, it is conceptually analogous to normalization in relational data models. Getting only one unambiguous implementation for a piece of code is the primary objective of refactoring; as such, it is much harder to achieve than it might seem at first.


Image Note

Ironically, the opposite of DRY is WET, an acronym for something like “write everything twice.” In accordance with the true meaning of DRY, we suggest rephrasing WET as “wire everything twice.”


Tell, don’t ask

Overall, Tell-Don’t-Ask is the inspiring principle of object modeling. At the foundation of OOP, there was Stroustrup’s idea of creating software entities that could incorporate data and expose some behavior. That’s precisely the point behind Tell-Don’t-Ask.

When designing an interface, avoid asking for data to be processed in some outermost logical container; instead, tell the interface to perform some action on some explicit data. Bundling together data and behavior helps you with modeling because it more closely represents the observed reality. To fully make sense of Tell-Don’t-Ask, consider the following everyday scenario:

Your significant other asks something along the lines of this: “Honey, what are you doing right now? Are you busy?”

Your significant other plays the role of the outermost logical container and is planning to orchestrate you in some process she’s eager to arrange. The point is that you might or might not be able (or willing) to do the task. But you are requested to provide data about your internal state without knowing the reason that data is required. Your answer might lead your significant other to command you to assist regardless of your situation or to find an alternate way of accomplishing the task.

Wouldn’t it be simpler and easier for everybody if she just told you what to do?

Honey, I need you to take the trash out.

The significant other—the orchestrator—only needs to keep track of whether the task has been accomplished or not in the allowed time. All the details of how it happens are delegated to and encapsulated in the object receiving the command.

Use of patterns

When facing a problem, a developer will certainly draw from the well of personal experience for any solutions to similar problems that worked in the past. Such building blocks are nothing more than hints and represent the skeleton of a solution. However, these same building blocks can become more refined day after day and generalized after each usage to become applicable to a wider range of problems and scenarios. Such building blocks might not provide a direct solution, but they usually help you to find your (right) way. And using them is usually more effective and faster than starting from scratch.

These building blocks are known as patterns.

The word pattern is one of those overloaded terms that morphed from its common usage to assume a specific meaning in computer science. According to the dictionary, a pattern is a template or model that can be used to generate things—all kinds of things. In computer science, we use patterns in design solutions at two levels: implementation and architecture.

What’s a design pattern, exactly?

We software professionals can attribute the concept of design patterns to an architect—a real architect, not a software architect. In the late 1970s, Christopher Alexander developed a pattern language with the purpose of letting individuals express their innate sense of design through a sort of informal grammar. From his work, here’s the definition of a pattern:

Each pattern describes a problem which occurs over and over again in our environment, and then describes the core solution to that problem, in such a way that you can use the solution a million times over, without ever doing it the same way twice.

Nicely enough, although the definition was not written with software development in mind, it applies perfectly to that. So what’s a design pattern?

A design pattern is a known and well-established core solution applicable to a family of concrete problems that might show up during implementation. A design pattern is a core solution and, as such, it might need adaptation to a specific context. This feature becomes a major strength when you consider that, in this way, the same pattern can be applied many times in many slightly different scenarios.

Design patterns are not created in a lab; quite the reverse. They originate from the real world and from the direct experience of developers and architects. You can think of a design pattern as a package that includes the description of a problem, a list of actors participating in the problem, and a practical solution.

The primary reference for design patterns is GoF. Another excellent reference we want to recommend is Pattern-Oriented Software Architecture by Frank Buschmann et al. (Wiley, 1996).

How to work with design patterns

Here is a list of what design patterns are not:

Image Design patterns are not the verb and should never be interpreted dogmatically.

Image Design patterns are not Superman and will never magically pop up to save a project in trouble.

Image Design patterns are neither the dark side nor the light side of the Force. They might be with you, but they won’t provide you with any special extra power.

Design patterns are just helpful, and that should be enough.

You don’t choose a design pattern; the most appropriate design pattern normally emerges out of your refactoring steps. We could say that the pattern is buried under your classes, but digging it out is entirely up to you.

The wrong way to deal with design patterns is by going through a list of patterns you might find in books and matching them to the problem. Instead, it works the other way around. You have a problem and you have to match the problem to the pattern. How can you do that? It’s quite simple to explain, but it’s not so easy to apply.

You have to understand the problem and generalize it.

If you can take the problem back to its roots and get the gist of it, you’ll probably find a tailor-made pattern just waiting for you. Why is this so? Well, if you really reached the root of the problem, chances are that someone else did the same in the past 20 years (the period during which design patterns became more widely used). So the solution is probably just there for you to read and apply.

This observation prompts us to mention the way in which all members of our teams use books on design patterns. (By the way, there are always plenty of such books scattered throughout the office.) Design patterns books are an essential tool. But we never read such books. We use them, instead, like cookbooks.

What we normally do is stop reading after the first few pages precisely where most books list the patterns they cover in detail inside. Next, we put the book aside and possibly within reach. Whenever we encounter a problem, we try to generalize it, and then we flip through the pages of the book to find a pattern that possibly matches it. We find one much more often than not. And if we don’t, we repeat the process in an attempt to come to a better generalization of the problem.

When we find the pattern, we start working on its adaptation to our context. This often requires refactoring of the code which, in turn, might lead to a more appropriate pattern. And the loop goes on.

Where’s the value in patterns, exactly?

Many people would agree in principle that there’s plenty of value in design patterns. Fewer people, though, would be able to indicate what the value is and where it can be found. Using design patterns, per se, doesn’t make your solution more valuable. What really matters, at the end of the day, is whether or not your solution works and meets requirements.

Armed with requirements and design principles, you are up to the task of solving a problem. On your way to the solution, though, a systematic application of design principles to the problem sooner or later takes you into the immediate neighborhood of a known design pattern. That’s a certainty because, ultimately, patterns are solutions that others have already found and catalogued.

At that point, you have a solution with some structural likeness to a known design pattern. It is up to you, then, to determine whether an explicit refactoring to that pattern will bring some added value to the solution. Basically, you have to decide whether or not the known pattern you found represents a further, and desirable, refinement of your current solution. Don’t worry if your solution doesn’t match a pattern. It means that you have a solution that works and you’re happy with that. You’re just fine. You never want to change a winning team!

In summary, patterns might be an end when you refactor according to them, and they might be a means when you face a problem that is clearly resolved by a particular pattern. Patterns are not an added value for your solution, but they are valuable for you as an architect or a developer looking for a solution.

Refactoring

Refactoring is the discipline of restructuring existing code in such a way that the external behavior remains unaltered. You refactor your code not because it doesn’t work but just to better address some nonfunctional attribute—such as readability, maintainability, testability, or extensibility—or even to improve performance.

Even though the term refactoring might lead some people to think of sophisticated practices, for the most part refactoring is the continuous performance of a few micro-operations. In addition, some of these operations are mechanically performed for you by ad hoc tools like ReSharper. Here are a few common refactoring operations:

Image Extract method Move a bunch of lines to a newly created method, thus making the original method shorter and promoting readability and code reuse.

Image Extract interface Turn the public methods in an existing class into a newly created interface. In this way, you promote interface programming and lower coupling between modules.

Image Encapsulate field Wrap a field in a class with a pair of getter and setter methods.

Refactoring also means renaming classes and methods throughout the codebase and moving classes to different namespaces. This is a delicate activity where code assistant tools really shine; without an automatic tool, renaming can really be painful. But renaming is critical to improving code readability, as you’ll see in more detail in the next chapter.

In the end, refactoring can be summarized in two points. One is continuous code editing improves readability and design. The other is performing more ambitious restructuring aimed at making the existing code adhere to a specific pattern.

Defensive programming

As Murphy used to say, if something can go wrong, it will. And Murphy was not a software engineer. As developers, we know the pattern very well. We mentioned the Ariadne 5 disaster in Chapter 1, “Architects and architecture today.” It all happened because developers didn’t write their code defensively. Defensive programming is about making the software work in a predictable manner in spite of unexpected inputs.

Concretely speaking, defensive programming is all about carefully checking input data in each and every method you have and making it clear through documentation and other means what each method actually produces. This can be done in a couple of ways—an old way and a more modern and effective way.

The If-Then-Throw pattern

An old but good practice of software development recommends that you place at the top of each method—before any significant behavior takes place—a barrier of conditional statements. Each conditional statement checks a different condition that input values must verify. If the condition isn’t verified, the code throws an exception. This pattern is often referred to as If-Then-Throw.

To be honest, we’ve heard If-Then-Throw labeled as a pattern, but we don’t think a formal definition exists for that. Maybe we should simply refer to it as a practice? Anyway, it’s not rocket science and is mostly about common sense. The idea is that the first lines of a method should be devoted to checking the validity of method arguments. In this way, if any invalid data is passed, you fail as quickly as possible without proceeding any further in the code. The application of this pattern is particularly important for constructors because it ensures that the state of the object is valid. You don’t need to use the pattern for private methods because these methods can be called only from the class itself and likely from the same author. Here’s an example: you pass every argument through a Boolean expression that ascertains whether the value is acceptable. If it is not, you just throw an exception:

public class Match()
{
public Match(String id, String home, String visitors)
{
// If-Then-Throw implementation
if (String.IsNullOrWhitespace(id))
throw new ArgumentException("id");
if (String.IsNullOrWhitespace(home))
throw new ArgumentException("home");
if (String.IsNullOrWhitespace(visitors))
throw new ArgumentException("visitors");

...
}

...
}

Extensive use of the If-Then-Throw pattern serves the purpose of evaluating the preconditions for a public method to run. It says nothing about the output generated and invariant conditions. At the end of the day, the If-Then-Throw pattern is a useful little thing especially if you apply it extensively to nearly any piece of data you receive from outbound calls.

For stricter and more effective control over the behavior of a class as a whole, you might want to look into something that Bertrand Meyer called Design by Contract. Design by Contract is an approach to programming based on the idea that each piece of software has a contract in which it formally describes what it expects and what it provides. The If-Then-Throw pattern nearly covers the first part of the contract, but it entirely lacks the second part.

Software contracts

Design by Contract describes the interaction between software components as a contract, where obligations and benefits are clearly expressed and enforced. Design by Contract isn’t natively supported in any mainstream programming language. However, frameworks exist to let you taste flavors of it in commonly used languages such as Java, Perl, Ruby, JavaScript and, of course, the Microsoft .NET Framework languages. In .NET, you create software contracts via the Code Contracts library added to the .NET Framework 4.

In the implementation of a software contract—regardless of the syntax details—any time you write a class method, you should be sure you can answer the following questions:

Image Under which conditions can the method be invoked?

Image Which conditions are verified after the method terminates?

Image Which conditions do not change before and after the method execution?

These three questions are also known, respectively, as preconditions, postconditions, and invariants. Let’s go a bit deeper into software contracts using the .NET implementation—Code Contracts—as a reference.

Preconditions

Preconditions refer to the Boolean conditions that must be verified for the method to start its execution. Typically, preconditions set conditions on input parameters and the current state of the class that exposes the method. Conceptually, preconditions are the same as the If-Then-Throw pattern. Have a look at the following code excerpt. It comes from a class—named Match—that describes any team sport game with periods (for example, basketball, football, water polo, and so on).

public Match(String id, String team1, String team2)
{
Contract.Requires<ArgumentException>(id.IsAlphaNumeric(IdLength, IdLength));
Contract.Requires<ArgumentException>(!team1.IsNullOrWhitespace());
Contract.Requires<ArgumentException>(!team2.IsNullOrWhitespace());
...
}
public Match EndPeriod()
{
Contract.Requires<ArgumentException>(IsInProgress());
Contract.Requires<ArgumentException>(IsBallInPlay);
Contract.Ensures(!IsBallInPlay);

IsBallInPlay = false;
if (CurrentPeriod == LastPeriod)
Finish();
return this;
}

The constructor expressly requires that its id argument be alphanumeric and of the specified length. If it is not, it throws an ArgumentException exception. Similarly, team names must be neither empty nor null; otherwise, the same exception is thrown. Contract.Requires<T> is the .NET way to express a precondition and bind it to a custom exception if the condition fails.

There a few interesting things to notice here. First and foremost, you express Boolean guards in the positive way, whereas you have them in the negated form if you opt for the If-Then-Throw pattern. This works also as a form of documentation and as a guide for testers. Second, the syntax is particularly compact and custom, and if they are aptly named, functions can be used to keep the readability of the code high.

Preconditions on the constructor check only input data, but if you look at the method EndPeriod, you see that preconditions can also be applied to the internal state of the object. You can’t call a period if game is not in progress and the ball is not in play.

Postconditions

Postconditions refer to the output generated by the method and the changes produced to the state of the object. A postcondition is a Boolean condition that holds true upon exit from the method. Managing postconditions manually can be cumbersome, but .NET Code Contracts really makes it a piece of cake.

Here’s a short but illustrative example: the method Sum of a calculator class. Without preconditions, the method looks like this:

public Int32 Sum(Int32 x, Int32 y)
{
// Shortcut exit (optimizing 2*x)
if (x == y)
return x << 1;

// Perform the operation and return
var result = x + y;
return result;
}

The method has two exit points. If you want to manually evaluate a postcondition, you need to save somewhere the value being returned, pass it by the Boolean evaluator, and then decide whether to throw or return. Here’s what the final code might look like:

public Int32 Sum(Int32 x, Int32 y)
{
// Shortcut exit
if (x == y)
{
// Perform the operation
var temp = x << 1;

// Evaluate postcondition
if (temp <0)
throw new ArgumentException();

return temp;
}

// Perform the operation
var result = x + y;

// Evaluate postcondition
if (result <0)
throw new ArgumentException();

return result;
}

Using Code Contracts the resulting code is surprisingly clean:

public Int32 Sum(Int32 x, Int32 y)
{
Contract.Ensures(Contract.Result<Int32>() >= 0);

if (x == y)
return x << 1;

var result = x + y;
return result;
}

The Ensures method captures the value being returned and ensures it is not negative; if the value is negative, the method throws a ContractFailedException exception.

Invariants

Invariants refer to a Boolean condition involving members of the class that do not change during the execution of any public methods, including constructors and setters. Invariants are used in all those scenarios where it is essential to avoid leaving objects in a logically inconsistent state. Have a look at the sample News class:

public class News
{
public String Title {get; set;}
public String Body {get; set;}

[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(!String.IsNullOrEmpty(Title));
Contract.Invariant(!String.IsNullOrEmpty(Body));
}
}

The Invariant method specifies that neither Title nor Body can be empty or null. Expand this to any set of business rules for a domain class—you have an easy way to ensure that your objects are always in a valid state. If some code that uses News sets, say, Title to null, immediately an exception for a contract violation will be thrown. More importantly, you don’t have to write a single line of code to check invariants after any call of a public method. That’s the magic of the .NET Framework Code Contracts implementation.


Image Note

We regularly use preconditions and often postconditions. We find invariants to be sometimes too restrictive with their extremist, strictly no-compromise vision of the code. But that’s just our experience, and experience sometimes comes out of the level knowledge of things you have.


Magic of .NET code contracts

In .NET, you express software contracts using a high-level syntax centered on a bunch of static methods out of the Contract class. However, the code that actually gets compiled is slightly different. If you use a decompiler (for example, dotPeek or .NET Reflector) to snoop into the code of the Contract class, you find that each method is nearly void and just throws an exception. Where’s the trick, then?

An additional tool integrated into the build process of Visual Studio—the Code Contracts rewriter—does the trick of reshaping any code of yours that uses contracts, understanding the intended purpose of expressed preconditions and postconditions and expanding them into proper code blocks placed where they logically belong. In the end, the rewriter alters the output of the compiler and expands preconditions in an actual block of If-Then-Throw statements, moves If-Then-Throw blocks for postconditions to each detected exit point, and places If-Then-Throw blocks around invariant conditions past the invocation of each public method. All this work is transparent to you and performed as a post-compile step.


Image Important

For reasons we could never understand completely, the Code Contracts API is a native part of the .NET Framework. However, the rewriter is not part of the .NET Framework download. It is a separate download you can get through the Visual Studio Gallery extension athttp://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970.


Summary

Especially in the past two decades, a lot has been done in the Information Technology (IT) industry to make a systematic approach to software development possible. Methodologies, design principles, and finally, patterns have been developed to help guide architects to envision and build systems of any complexity in a disciplined way.

In the beginning, we chased a disciplined way of coding for the sake of elegance and design. It soon turned into a more serious matter of survival and success-or-fail. We discovered that maintainability is king. If you can favor maintainability over everything else, well, that’s the best deal ever and the most beneficial compromise you can get. With maintainability, you can have anything else. But why is software maintenance so expensive?

Maintenance becomes expensive if, for whatever reason, you produced unsatisfactory software, you haven’t tested it enough, or both. More often than not, though, maintenance becomes the standard way of working because requirements are not laid out well, requirements are released frequently, and prototypes and frequent builds are requested. Which attributes make software easier to maintain and evolve? At the top of the list is structured design, which is best applied through proper coding techniques.

In this chapter, we outlined principles of software design, cataloguing them in universal principles, object-oriented design principles, and then SOLID principles and coding vectors. While principles address a general point, design patterns represent a more structured solution for a recurring problem. Taking code from its current state to a better state is the goal of refactoring. The ease of refactoring is the measure of how maintainable your code is. Every successful step of refactoring takes your project one step away from the “Big Ball of Mud.”

In addition to structured design, code readability is another fundamental asset for code. Readability, as well as testability and extensibility, are aspects of code we address in the next chapter.

Finishing with a smile

Speaking of principles and software attributes, we reserved the spotlight for maintainability. Sometimes it seems that maintainability is used as an excuse to take dependency injection to its extreme: every class is an injectable component. Or any code you write is JavaScript-like.

See http://www.murphys-laws.com for an extensive listing of computer (and noncomputer) related laws and corollaries. Here are a few you might enjoy:

Image An expert is someone brought in at the last minute to share the blame.

Image If it seems too good to be true, it probably is.

Image The probability of bugs appearing is directly proportional to the number and importance of people watching.