13. Design Principles 2

Keep it simple:

Leave decisions to the last responsible moment: delay the decision so that you have the most information.

Refactoring

Leave the world a better place than you found it.

Martin Fowler abridged: refactoring makes small behavior-preserving transformations, each of which are ‘too small to be worth doing’ but cumulatively have a significant effect.

Object-Oriented Design

Data modelling: focus on data internal to the object

Behavior modelling: focus on services provided to the external world

OO models both; just a question of where you start from.

Inheritance: The Dark Side

Mistakes:

Principle: if it can change, it isn’t inheritance.

Using Inheritance for Implementation

Favour composition over inheritance.

e.g. instead of stack inheriting from vector, stack should have the stack as a private variable; the data store for the stack.

‘Is a-role-of’

Example inheritance tree:

             Person
   Student              Staff
   Postgrad       Tutor  Admin  Lecturer
                                Professor

What if a person is both a postgrad and tutor?

Instead, have each Person have multiple Roles. In the real world, when a person changes role, the person doesn’t change.

This allows for separation of concerns.

The ‘Becomes’ Problem

e.g. EligibleStudent and IneligibleStudent inherit from Student. What happens if a student becomes ineligible: the object must switch class becomes of a relatively trivial detail.

Just have eligibility as a boolean in the Student class.

Principle: Inheritance isn’t dynamic.

Over-Specialization

Method arguments etc. should use the most general interface/class you can get away with.

Violating the Liskov Substitution Principle

LSP: the behavior of a method shouldn’t change regardless of the subclass of arguments it is given.

e.g. setWidth, setHeight method for a Rectangle. Square is a subclass of Rectangle. Method taking Rectangle as argument could be given a Square: if it sets the width, the height will be set silently; area/perimeter calculations will give unexpected results.

If methods start telling white lies, you start walking along the path to hell.

Single Responsibility Principle

Every module or class should have responsibility over only one part of the functionality - this responsibility should be fully encapsulated.

Why?

Typically found in controllers, initializers.

Interface Segregation Principle

No client should be forced to depend on methods/interfaces it doesn’t use.

e.g. iPhone interface can be split into widescreen iPod, phone, internet communicator device interfaces.

Dependency Inversion Principle

1: When high-level modules depend on low-level modules; both should depend on abstractions.

2: Abstractions should not depend on details; details should depend on abstractions.

i.e. high-level objects should not depend on low-level implementations. Use abstractions; no one likes micro-managers.

When things change, you want as little stuff around it to change.

To do this, try to avoid new; explicitly instantiating concrete instances.

Dependency Injection

Passing objects (that conforms to the broadest interface you can get away with) through a constructor rather than creating a concrete instance of it yourself.

SOLID Principles

Single responsibility.

Open-closed principle.

Liskov substitution principle.

Interface segregation principle.

Dependency inversion.