left-icon

Object-Oriented Programming in C# Succinctly®
by Sander Rossel

Previous
Chapter

of
A
A
A

CHAPTER 5

General Responsibility Assignment Software Patterns or Principles (GRASP)

General Responsibility Assignment Software Patterns or Principles (GRASP)


One set of patterns that you aren’t going to see a lot are the GRASP patterns, General Responsibility Assignment Software Patterns or Principles (I’m pretty sure the abbreviation came first). They are not quite patterns like Design Patterns, they are more like advice that should help you solve common programming problems. I’m not sure why GRASP is not as popular as SOLID or Design Patterns, as the principles are no less important and every developer should know them. Craig Larman discusses them in his book Applying UML and Patterns – An Introduction to Object-Oriented Analysis and Design and Iterative Development[4]. There are a total of nine GRASP principles, and some of them overlap with what was already discussed. In this chapter I’ll discuss the GRASP Patterns that have no overlap with what we previously discussed. The ones that overlap are Controller (like we’ve seen in MVC), Polymorphism (one of the three pillars of OOP), Indirection (another word for Dependency Inversion), and Protected Variations (another form of Dependency Inversion and Encapsulation).

Creator

One of the first problems you will encounter with any OO application is that you need to create an instance of some object, but you’re not sure what other object is going to create it. In some cases this is obvious, such as when working with certain Design Patterns such as Factory Method or Singleton. Other times it’s not as obvious. At the start of this book, we saw a small example with a Car and an Engine class (Inheritance vs. Composition).

Code Listing 91: Car and Engine

public class Engine

{

    // ...

}

public class Car

{

    private Engine engine = new Engine();

    // ...

}

A Car has an Engine, so it should obviously create an Engine, right? Well, maybe. There are a few pointers for whether one class creates another. If some class contains or aggregates another class, closely uses another class, or has the initializing data for another class (see also Information Expert in the next section), it might be a good idea to have that class create another class. A Car does contain an Engine, but it does not aggregate it. A Car closely uses an Engine. Does the Car also have the initializing data for an Engine? Who knows? It depends on your design. In this case I would probably go for another solution, which is to compose the Car with an Engine class through the constructor (or through some other means).

Code Listing 92: Different Engines

public abstract class Engine { }

public class GasolineEngine : Engine { }

public class DieselEngine : Engine { }

public class Car

{

    private Engine engine;

    public Car(Engine engine)

    {

        this.engine = engine;

    }

    // ...

}

And now that another class creates an Engine, you can give your Car any Engine you want.

Code Listing 93: Compose your Car

Car diesel = new Car(new DieselEngine());

Car gas = new Car(new GasolineEngine());

Of course, that still doesn’t solve the entire problem, because who creates the Car, and does that same class or yet another create the Engine?

It is important to note here that these objects are code objects and not domain objects. The domain, or real world objects, are probably created on some assembly line. Chances are you haven’t modeled the assembly line in your code, so that’s probably not going to create the Car or Engine. One thing you know for sure, a Car has never created an Engine in the real world.

Information Expert

Information Expert is a principle that you are probably already applying. Information Expert addresses the problem of which object handles which responsibility. How often have you wondered what the right class was for a certain method? The answer lies with Information Expert and is deceptively simple. A responsibility should be assigned to the class that has all the information necessary to fulfill that responsibility.

Let’s take a look at an example. Earlier we created an Adapter for a SqlServerApi class. Here’s a small reminder:

Code Listing 94: The SqlServerApiAdapter

public class SqlServerApiAdapter : IDbApiAdapter

{

    private SqlServerApi api = new SqlServerApi();

    public object GetData()

    {

        DataTable table = api.GetData();

        object o = ConvertToObject(table);

        return o;

    }

    private object ConvertToObject(DataTable table)

    {

        // ...

        return null; // Placeholder.

    }

    public void SendData(object data)

    {

        DataTable table = ConvertToDataTable(data);

        api.SendData(table);

    }

    private DataTable ConvertToDataTable(object obj)

    {

        // ...

        return new DataTable();

    }

}

public class OracleApiAdapter : IDbApiAdapter

{

    private OracleApi api = new OracleApi();

    public object GetData()

    {

        return api.GetQuery();

    }

    public void SendData(object data)

    {

        api.ExecQuery(data);

    }

}

Now as it turns out we want to be able to create new databases. That is new functionality as it is not supported by GetData or SendData. Now where could we put this new CreateDatabase method? Maybe in our Program class?

Code Listing 95: SqlServerApi in Program

class Program

{

    static void Main(string[] args)

    {

        CreateDatabase("MyDB");

        SqlServerApiAdapter adapter = new SqlServerApiAdapter();

        object data = adapter.GetData();

        // ...

    }

    static void CreateDatabase(string name)

    {

        SqlServerApi api = new SqlServerApi();

        api.CreateDatabase(name);

    }

}

I think you can see the problem here. We’ve gone through all the trouble of creating an Adapter for the SqlServerApi and now we’re going to use it directly in our application. It makes no sense, as SqlServerApiAdapter is the much more obvious choice. It already uses SqlServerApi anyway! And that’s exactly the point of Information Expert. The SqlServerApiAdapter has all the information that’s necessary to create a new database, so it is a good candidate for getting this new functionality. The alternative, Program, knows nothing about SqlServer or databases, so the choice is easily made.

The example here is kind of obvious, but it demonstrates what Information Expert is all about. I’ll tell you a little secret too: I’ve seen software that had CreateDatabase in Program (well, not exactly that, but a likewise situation). When a code base grows large, it is not always obvious where new functionality goes.

Low Coupling

The Low Coupling principle helps in keeping the impact of changes to a minimum. Coupling between classes is when a class depends upon another. Needless to say that when a lot of classes depend upon a certain class it becomes difficult to change that class. The Information Expert Principle actually helps towards this end. Let’s check the previous example with the SqlServerApi. Putting the CreateDatabase method in Program was bad design because Program didn’t know about SqlServerApi and shouldn’t need to know about it. It created unnecessary coupling between SqlServerApi and Program.

Sequence Diagram of High Coupling

Figure 21: Sequence Diagram of High Coupling

As you can see in the diagram, there is a direct line between the Program and the SqlServerApi. If the SqlServerApi changes, it is possible we need to change Program and SqlServerApiAdapter. In the other design, where the SqlServerApiAdapter creates the database, we get another figure.

Sequence Diagram of Low Coupling

Figure 22: Sequence Diagram of Low Coupling

In this design the Program depends upon SqlServerApiAdapter and the SqlServerApiAdapter depends upon SqlServerApi. Now if SqlServerApi changes we only need to change SqlServerApiAdapter. Of course, if the change in SqlServerApi causes another (breaking) change in SqlServerApiAdapter we need to change Program too.

As you see, the Low Coupling principle can be used to compare different design solutions and we should, in most situations, pick the design which has the lowest coupling between classes. Because Information Expert guides you in finding the class that has all the necessary information for a certain responsibility, it helps lead to a design with the least coupling.

High Cohesion

Cohesion is the degree to which certain elements of a class belong together. The High Cohesion Principle states that a class’ cohesion should be high. In other words, the methods and properties of a class should belong together. Let’s look at another example. Suppose we have a Person class with some properties and methods:

Code Listing 96: A Person Class

public class Person

{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime DateOfBirth { get; set; }

    public string GetFullName ()

    {

        return String.Format("{0} {1}", FirstName, LastName);

    }

    public int GetAge()

    {

        int age = DateTime.Today.Year - DateOfBirth.Year;

        // Month and Day correction.

        if (DateOfBirth.AddYears(age) > DateTime.Today)

        {

            age -= 1;

        }

        return age;

    }

}

Cohesion is high because all properties and methods describe a person. Let’s add a method.

Code Listing 97: Person with SalesOrders

public class Person

{

    // ...

    public List<SalesOrder> GetSalesOrders()

    {

        // ...

    }

}

The cohesion of Person just went down. It’s not completely wrong, but you should ask yourself if a Person really needs to know about a SalesOrder. It’s not unthinkable that you want to use the Person class in a context where you don’t also want to depend on the SalesOrder class. Maybe we could delegate the retrieval of SalesOrders for a Person to some other class.

Code Listing 98: A SalesOrderRepository

public class SalesOrderRepository

{

    public List<SalesOrder> GetPersonsSalesOrders(Person person)

    {

        // ...

    }

}

Usage would now look as follows:

Code Listing 99: Usage of the SalesOrderRepository

Person sander = new Person

{

    FirstName = "Sander",

    LastName = "Rossel",

    DateOfBirth = new DateTime(1987, 11, 8)

};

SalesOrderRepository repo = new SalesOrderRepository();

List<SalesOrder> orders = repo.GetPersonsSalesOrders(sander);

I’m not saying this is the best solution, but it does increase the cohesion, and lower the coupling, of the Person class.

As you see, High Cohesion and Low Coupling go hand in hand. When the cohesion of a class is high the coupling is typically low. Again, the Information Expert and Low Coupling Principles lead towards a design with the highest cohesion.

Pure Fabrication

We’ve seen Pure Fabrication a lot already. In fact, it’s what you do when some responsibility doesn’t fit in any class you already have: you make up a new one specifically designed for this one task. In the previous example, we created a SalesOrderRepository because getting SalesOrders for a person in the Person class violated low coupling and high cohesion. Basically, any class that does not model a domain object is a pure fabrication (a fabrication of our imagination that is pure because it has only one responsibility).

Let’s look at another example. We still have our Person class. Now what class is going to save our Person to the database? We don’t have any database classes yet and a Person has most of the information necessary to save itself to the database. So it would seem alright to have the Person class do the database logic necessary to save itself to the database. We already know that’s not quite a good idea, mostly because we want our Person to have low coupling and high cohesion.

Code Listing 100: Person Class that Breaks Low Coupling and High Cohesion

public class Person

{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime DateOfBirth { get; set; }

    public void Save()

    {

        // Database logic here.

    }

}

So we go about and create a pure fabrication, a class with the sole purpose of saving a Person to the database. I won’t include the code here because I think you get the point. When we apply other patterns and principles we’ve seen in this book, we end up with a Data Mapper and, possibly, a Repository.

The Takeaway

GRASP is really useful when designing classes. Unlike anything else we’ve seen so far, they aren’t of a very technical nature, and they don’t describe classes or methods. They describe how to get an efficient design for your software. This chapter has shown that there is not one design for an application. In fact, when we only had a Car and an Engine we already had two design choices (and even more less obvious ones). Keeping GRASP in mind, we can get to some design that’s at least better than many of the alternatives (there is no such thing as a “best design”). The fun thing about GRASP is that a lot of it is already common sense to most developers (like a Person shouldn’t save itself), but with GRASP you have some formal rules that describe why it makes such common sense.

Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.