Design Modeling

Design Metrics

Design Principles

Architectural Patterns

Layered Architecture

Problem

Where to begin?

Solution

Organize your application into layers. Layer N depends only on Layer N-1. The most common approach is the three-layer architecture:

Roles:

Presentation Layer = user interface

Domain Layer = business logic, rules, data

Data Layer = provide persistence for domain layer, access to databases, file system

Variations:

Tier = a remote layer

Model-View-Controller Architecture

Problem

Presentation logic tends to be volatile, while domain logic tends to be stable. Mixing the two therefore de-stabailizes the domain logic.

Solution

Roles:

Model = domain layer or domain layer facade
View = user output, displays model
Controller = user input, manipulates model

Variations:

Page Controller = 1 controller/view
Front Controller = 1 controller for all views
Use Case Controller = 1 controller/use case
Command Processor

Open Architecture

Problem

How can we allow third-party developers to provide extensions of our product directly to our customers?

Solution

Structural Patterns

Facade

Problem

Assume a client package uses a provider package:

For example:

client = presentation tier
provider = business tier

client = business tier
provider = data tier

Assume the client package contains the following classes:

client.C1, client.C2, client.C3

Assume the provider package contains:

provider.P1, provider.P2, provider.P3, provider.P4, proviader.P5

In the worst case scenario, every client class depends on every provider class:

The coupling degree between the client package and the provider package is too high. The client knows too much about the structure of the provider package, making it difficult to modify the provider package.

Solution

Introduce a Facade class to the provider package. This is the only class that has public scope, the rest of the classes in the provider package have package scope. The facade receives all requests from the client package. The facade choreographs all major transactions with the provider package:

Gateway/Proxy

Problem

A provider is a remote resource such as a server or database. Worse, the resource is not object-oriented. We don't want to distribute knowledge of the provider throughout the client.

Solution

Introduce a gateway that implements an object-oriented API while managing the connection to the remote resource:

Mediator

Problem

Assume a package contains classes P1, P2, P3, P4, P5. In the worst case, each class in the package depends on every other class in the package:

There are too many dependencies. Every class depends on every other class.

Solution

Introduce a mediator to the package. Replace all of the dependencies for each class with a single dependency on the mediator:

Think of the mediator as a virtual post office. It maintains a registry of names of classes and their locations. If an instance of P1 wants to send a message to an instance of P2, it only needs to know the name of this instance, not its location.

class Mediator {
   Map<String, Provider> registry;
   void send(String msg, String receiver) { ... }
   ...
}

The mediator idea can be extended to a yellow pages server. In this variant the registry pairs service descriptions with service providers. The client only needs to know the type of service he wants, not the name of the service provider.

Behavioral Patterns

Adapter

Problem

Solution

Strategy

Problem

Suppose an Employee class has a method for computing the employee's annual bonus:

class Employee {
   Money computeBonus() { /* skimpy default bonus */ }
   // etc.
}

Different subclasses of Employee: Manager, Programmer, Secretary, etc. may want to override this method to reflect the fact that some types of employees (managers) get more generous bonuses than others (secretaries and programmers):

class Manager extends Employee {
   Money computeBonus() { /* gerenous bonus */ }
   // etc.
}

There are several problems with this solution.

1. All programmers get the same bonus. What if we wanted to vary the bonus computation among programmers? Would we need to introduce a special subclass of Programmer?

class SeniorProgrammer extends Programmer {
   Money computeBonus() { /* gerenous bonus */ }
   // etc.
}

Note also that this leads to code duplication.

2. What if we wanted to change the bonus computation for a particular employee? For example, what if we wanted to promote Smith from programmer to senior programmer? Would this require us to recompile any code?

3. What if we decided to give all programmers the same generous bonus that managers get? What changes would we need to make? Should "generous bonus" become the new default algorithm that is overridden in the Secretary class with the skimpy bonus algorithm? Should we copy and paste the "generous bonus" algorithm from manager to Programmer?

Solution

We can use the Strategy pattern and introduce a separate hierarchy of bonus calculators:

Now different employees can have different bonus calculators, regardless of the class they instantiate. Even better, the bonus calculator used by a particular employee can be changed dynamically.

When we begin to override inherited methods, the is-a semantics implied by the extends relationship begins to break down. At what point does inheritance become a nuisance? At what point does the behavior of a manager diverge from the default behavior of an employee that we can no longer say that a manager is an employee?

For more patterns see: http://www.mathcs.sjsu.edu/faculty/pearce/patterns2/