Tagged: object-oriented software design

UML Class Diagram

Class diagram is a very important part of UML. It’s a structure diagram and it’s purpose is to display classes in the system with all the relationships between them. In my opinion it’s the most popular type of diagram in software development.

Drawing class diagram of your design really helps to see the problem in broader terms. By writing it down you free space in your head for new ideas :-). It is also easier to understand by others when you want to discuss the problem with someone else. The thing is, I often find myself wondering about the syntax when I read someone else’s diagrams. That’s why I decided to make a little cheat sheet here to remind me.

Class

Kind of a key component in a class diagram. Classes will be shown as nodes and usually as boxes. Here is a example of one. Each class can have methods and attributes defined. The convention is shown on Figure 1.

UML diagram: A class

Figure 1: A class

Inheritance

Class inheritance is in terms of UML a relationship of generalization. It represents “is a” relationship on class level. Figure 2 shows how to portray generalization.

UML diagram: Inheritance

Figure 2: Inheritance

Realization

UML has different relationship for interfaces. When you inherit from an interface you implement it, which is in terms of UML a relationship of realization. It’s visual appearance is similar to inheritance, but the line is dashed. Also the interface class should be marked as abstract (have name written in italic). See on Figure 3.

UML diagram: Realization

Figure 3: Realization

Association

Another form of relationship in class diagram is association. It’s a object-level relationship (i.e. happens between objects of associated classes). So the whole relationship represents a family of links. There are multiple types of association with stronger policies (composition and aggregation).

UML diagram: Association

Figure 4: Association

Aggregation

Aggregation is a stronger and more specific form of association. It’s “has a” relationship. Graphical representation of aggregation is shown on Figure 5.

UML diagram: Aggregation

Figure 5: Aggregation

 Composition

Even stronger form of aggregation is composition. Instead of “has a” it represents “owns a”. It’s suited for relationship when one object can only exist as a part of another. For example if a plane has a wing it’s a composition. What would you do with a wing alone, right? The plane owns it. But when a  pond has some ducks in it it’s an aggregation. The ducks will survive without a pond (only probably not that happy). And a pond will still be a pond with or without ducks. Graphical representation of composition is virtually the same as aggregation, only the diamond is filled (see on Figure 6).

UML diagram: Composition

Figure 6: Composition

Dependency

Last type of relationship is a dependency. It’s weaker then association and it says, that a class uses another one and therefore is dependant on it. The use of dependency is appropriate for example in cases where an instance of a class is stored as a local variable inside another classes’ method. Or some static methods are used, so the classes are not associated, but one depends on the other.

UML diagram: Dependency

Figure 7: Dependency

Cheat Sheet

I did all the examples in an open-source diagram editor called Dia. I recommend it by the way. And because it’s such a wonderful editor, here’s a complete cheat sheet (if you’d like to print it).

UML Class Diagram Cheat Sheet

UML Class Diagram Cheat Sheet

Advertisements

Interface Segregation Principle in Software Design

ISP, not Internet Service Provider, but Interface Segregation Principle is the last of the famous principles of SOLID object-oriented software design. It was introduced by Robert C. Martin in his series of articles in 1996. Intention of this principle is to avoid creation of “fat” interfaces.

A fat (or polluted) interface comes from extending current interface with some functionality that is useful only to a subset of entities that depends on it. This phenomenon leads eventually to creation of dummy methods just to be able to use the interface. And that’s bad. Dummy methods are dangerous and also violate the LSP. The ISP as wrote the author declares that

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

Each interface should have clearly defined purpose and make reasonable abstraction of a part of the current problem. The best practice (in my opinion) is to use multiple inheritance when implementing the interfaces. This method will separate things that don’t logically belong together on the abstraction level and clean wrong dependencies in our code. But it also allow us to couple them back together in objects, that cover multiple things and work on the same data.

Let me show an example of how it should not look like. This is an interface for a car.

/* Bad example */
class CarOperation
{
    public:
        virtual void steer(int degrees) = 0;
        virtual void pullHandbrake() = 0;
        virtual void accelerate() = 0;

        virtual void shift(int gear) = 0;

        virtual void toggleAirConditioning() = 0;
};

There are a couple common things you can do with a car. Every car usually has a steering wheel, an acceleration pedal and possibly even a handbrake. But what about those cars with automatic transmission? They don’t allow the driver to shift gears, so what should they do with the shift method? The interface enforces it’s implementaion. And again with air conditioning. Some cars don’t have an air conditioner. The way here is to split CarOperationinterface into a couple smaller ones.

class BasicCarOperation
{
    public:
        virtual void steer(int degrees) = 0;
        virtual void pullHandbrake() = 0;
        virtual void accelerate() = 0;
};

class GearboxCarOperation
{
    public:
        virtual void shift(int gear) = 0;
};

class AirConditioningCarOperation
{
    public:
        virtual void toggleAirConditioning() = 0;
};

class AlfaRomeo166 : public BasicCarOperation, GearboxCarOperation, AirConditioningCarOperation
{
    /* Implementation of all the interfaces. */
};

class SkodaFavorit136L : public BasicCarOperation, GearboxCarOperation
{
    /* No air conditioning for old cars. */
};

The clients that will use the concrete cars won’t look at them directly as AlfaRomeo166 or SkodaFavorit136L. They will operate them through the interfaces. If some client function wants to turn on a air-conditioning it will look like this

void beCool(AirConditioningCarOperation* vehicle)
{
    vehicle->toggleAirConditioning();
}

That’s the beauty of interface segregation principle. You get exactly what you need, nothing more and nothing less, which makes the code easier to maintain, reuse and saves you from a cascade of unpredictable errors, when you decide to modify existing code.

Sources