Skip to content

SwArcDocDesignPatterns

Henry m edited this page Sep 13, 2024 · 1 revision

Design patterns

009_design_patterns.mp4

Design patterns good and bad:

  • good
    • vocabulary
    • design guide
  • bad
    • not an implementation guide
    • not a panacea
    • religion
    • Dont let it impede your creativity

The usefulness of patterns really comes to fruition when you combine them.

  • Strategy - Define a family of algorithms, encapsulate each one and make then interchangeable
    • Strategy allows the algorithm to vary independently from clients that use it.
  • Command - Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations
    • For when your language does not have higher order functions(provide functions as a parameter to other functions)

Introduction

References

Overview of Patterns

Name Category Intent
Chain of Responsibility Behavioral Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
Command Behavioral Encapsulate a request as an object, thereby letting you parameterize clients with different request, queue or log requests, and support undoable operations.
Interpreter Behavioral Given a language, define a representation for its grammar along with an interpreter that uses the reprensentation to interpret sentences in the language.
Iterator Behavioral Provide a way to access the elements of an aggregate object sequetially without exposing its underlying representation.
Mediator Behavioral Define an object that encapsulates how a set of object interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
Memento Behavioral Without violating encapsulation, capture and externalize an object's internal state so that the object can be restired to this state later.
Observer Behavioral Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
State Behavioral Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
Strategy Behavioral Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary indeopendently from clients that use them.
Template Method Behavioral Deine the skeleotn of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorith withou changin the alrgorith's structure.
Visitor Behavioral Represent an opreation to be performed on the elements of an object structure. Visitor lets you define a new operation without changin the classes of the elements on which it operates.
Abstract factory Creational Provide an interface for creating families of related or dependent objects without specifying their concrete class.
Builder Creational Separate the construction of a complex object from its representation so that the same construction process can create different representations.
Factory method Creational Define an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclass.
Prototype Creational Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
Singleton Creational Ensure a class only has one instance, and provide a global point of access to it.
Adapter Structural Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Bridge Structural Decouple an abstraction from its implementation so that the two can vary independently.
Composite Structural Compose objects onto tree structures to represent parth-whole hierachies. Composite lets clients treat individual objects and compositions of objects uniformly.
Decorator Structural Attach additional responsibilities to an object dynamically. Decorators procide a flexible alternative to subclassing for extending functionallity.
Facade Structural Provide a unified interface to a set of interfaces in a subsystem. Facade definees a higher-level interface that makes the subsystem easier to use.
Flyweight Structural Use sharing to support large number of fine-grained objects efficiently.
Proxy Structural Provide a surrogate or placeholder for another object to controll access to it.

Design patterns for dummies

Chapter 2

  • has-a: This class reference the other class.
    • If you use the “has-a” model, you can create a well-defined family of algorithms — as many as you need — and then choose the algorithm you want to use.
    • All kinds of code can use these algorithms because the code in these algorithms is no longer buried in the StreetRacer, Helicopter, and other classes.
    • you can change the behavior you want at runtime.
      • the “has-a” relationship can give you more flexibility than the “is-a” relationship when it comes to configuring behavior at runtime.
  • is-a: This class inherits from the other class.
    • If you use “is-a” inheritance, you may end up spreading out how you handle a particular task in the base class and all derived classes

Working with design patterns often means spending more time on the abstraction part of the process than on the concrete classes part.

design patterns tend to favor composition over inheritance. (You use composition when your object contains other objects instead of inheriting from them.) Inheritance sets up "is-a" relationships -- Rectangle "is-a" Shape, for example. As you’re going to see, however, that can introduce unexpected rigidity and problems into your code, especially when it comes time to maintain that code.

Design pattern-oriented programming often prefers object composition over inheritance.

When you use composition, your code contains other objects, rather than inheriting from them. And to be supple enough to deal with the various kinds of contained objects in the same way, with the same code, design-patterns often rely on polymorphism.

Four pillars of OOP:

  • Abstraction
    • All about breaking your approach to a problem into natural segments.
    • This is where you come up with the objects that divide hte problem into manageable parts.
    • The data items needed by each object become that object's properties, whether public or private.
    • The actions each object needs to perform in the real world become its actions in code.
  • Encapsulation
    • When you wrap methods and data up into an object, you encapsulate those methods and data.
    • When you encapsulate functionality into an object, you decide what interface that object exposes to the world.
    • One of the primary design insights here is that you should encapsulate what changes the most.
      • extracting the part of your code that changes the most, or that needs the most maintenance, and encapsulating that part into its own object for easier handling.
  • Polymorhism
    • the ability to write code that can work with different object types and decide on the actual object type at runtime.
    • class Rectangle extends Shape
  • Inheritance
    • the process by which one class can inherit methods and properties from another.

Separate the parts of your code that will change the most from the rest of your application and try to make them as freestanding as possible for easy maintenance. You should also always try to reuse those parts as much as possible.

if part of your application changes a lot, get it out of those large files that otherwise change very little and make the sections that change a lot as freestanding as you can so that you can make changes as easily as possible while reducing side effects. And if you can, reuse the separated components that change a lot so that if you need to make a change, that change will be made automatically throughout the many places in the code that use those components.

With inheritance, base classes and derived classes have an “is-a” relationship. That is, a Helicopter “is-a” Vehicle

With composites, you select and use the objects you want, instead of having a rigid hard-coded internal way of doing things. That gives you a “has-a” relationship with those objects — a street racer “has-a” certain way of moving, which is encapsulated in an object; a helicopter “has-a” different way of moving, which is also encapsulated in an object. And each object performs a task.

One object, one task often makes sense instead of writing multi-generation code where one task is spread out over a dozen generations. In other words, you’re reorganizing around the tasks, not around the generations of classes that inheritance gives you.

When planning for change, consider “has-a” instead of “is-a” relationships, and put volatile code in the objects your application contains, rather than inheriting that code.

In design pattern terms, each implementation of the go method is called an algorithm (basically that’s just another name for a strategy).

Chapter 3

As much as possible, make your code closed for modification, but open for extension. In other words, design your core code so that it doesn’t have to be modified a lot, but may be extended as needed.

Chapter 4

When you implement a design pattern, it’s often a good idea to start by creating an interface to make sure that the objects you create will adhere to the pattern you’re trying to use — especially if multiple objects are involved. Implementing that interface — programming to an interface as it’s called — keeps your code in line and usually keeps things clearer.

loose coupling: No more information is traded than need be, keeping the data space chatter down to a minimum.

When you hard code command handling throughout a single class, you can end up with a large, monolithic class that’s hard to debug or even understand. By breaking things out into encapsulated objects that communicate no more than they need to — using simple notifications — you can often gain a great deal of flexibility and robustness. By working with more self-contained objects, you’re able to debug and develop those semi-independent objects easier, making agile development easier.

Patterns

Table 4-3 of the PatternArch for dummies

Section Used for
Name Title of pattern (and a short summary)
AKA list of other names.
Example A real-world example that shows that the problem is real.
Context Where the patterns applies
Problem The problem that the pattern solves.
Solution The fundamental solution of the pattern.
Structure A detailed explanation of the structural aspects of the pattern.
Dynamics Typical runtime scenario
Implementation Guidelines for implementing the pattern.
Example resolved How the original problem was solved and key points that wer not raised in the other pattern section.
Variants Similar related variants or specializations of the pattern.
Known uses Real systems that are known to implement this pattern
Consequences Good and bad effects that this solution has on the problem and design
See also Patterns that are closely related or that solve similar problems.

Architectural patterns

Descriptions of Design patterns

Strategy pattern

This design principle comes into play when it makes sense to extract code that handles specific tasks from your app.

Encapsulate code in external algorithms for easy use rather than spreading it around inside your core code and modifying it throughout that code.

This design pattern is often used as an alternative to inheritance, where you can end up spreading out the way you handle one specific task over many class files.

The Strategy design pattern says that you should extract the volatile parts of your code and encapsulate them as objects; you can use those objects as you need them. Now you can customize your code by creating composites of objects. At runtime, you just use polymorphism to choose the object(s) you want to work with.

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it

The Strategy design pattern points out that, sometimes, it’s good to be taskoriented. That’s especially important if you want to maintain volatile code away from the main code for your app, or if you want to change the algorithm you use at runtime.

Consider using the Strategy design pattern if you have one of the following situations:

  • You have volatile code that you can separate out of your application for easy maintenance.
  • You want to avoid muddling how you handle a task by having to split implementation code over several inherited classes.
  • You want to change the algorithm you use for a task at runtime.

any time you start to get task-oriented and want to make those tasks one of the main design points of your code, the Strategy design pattern should spring to mind.

Decorator pattern

ch3.

Instead of using external algorithms, this design pattern is all about using wrapper code to extend your core code.

Decorator pattern: Solution

C++

class ComponentDecorator extends Computer {
    public:
    virtual String description() = 0;
}

Java:

public abstract class ComponentDecorator extends Computer {
    public abstract String description();
}

Factory pattern

ch3.

The new operator is fine as far as it goes, but when your object creation code changes a lot, it’s time to think about factoring it out into factory objects.

Remember that one of our objectives is to make sure that the core code doesn’t have to be modified or has to be modified as little as possible.

You should derive all the objects your factory creates from the same base class or interface so that code that uses the objects it creates doesn’t have to be modified for each new object type.

According to the GoF book, the Factory Method design pattern should “Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses.

Consider the GoF Factory Method pattern when circumstances have gotten decentralized enough so that many programmers who subclass your factory class are overriding it so much that they’re changing it substantially.

Allows programmers to request objects and have the correct type created behind the scene and returned(C#: Design Patterns with Reynald Adolphe)

Observer pattern

When Editing a record, set myself up as an observer of that record.

It’s more flexible than hard coding everything into one unbreakable block and allows you to add or remove observers at runtime. It couples your objects loosely, which is something you should strive for, and builds composite objects using “has-a” relationships.

Chain of Responsibility pattern

Here’s another pattern that’s all about notifying other objects when something’s happened: the Chain of Responsibility design pattern.

This pattern is all about connecting objects in a chain of notification; as a notification travels down the chain, it’s handled by the first object that is set up to deal with the particular notification.

You use this pattern when not all your observers are created equal. For example, say that you have a layered application with a set chain of command for events — a mouse event may originate in a particular control, then bubble up to the control’s container, then the container’s window, and eventually up to the application itself. The first object that can handle the event correctly should grab it and stop the event from further bubbling.

Singleton pattern

ch5 With the Singleton design pattern, you have only one object of a particular class throughout your code.

Ensure a class only has one instance, and provide a global point of access to it.

You use the Singleton design pattern when you want to either restrict resource use (instead of creating numbers of large objects without limit) or when you have a sensitive object whose data shouldn’t be accessed by multiple instances (such as a registry).

Besides registry objects, you can use the Singleton pattern when you want to restrict the number of objects created because you want to share the data in those objects — as when you have a window object or dialog object that displays and modifies data, and you don’t want to create multiple objects, which might confuse access to that data.

Creating a single object can also be important when you’re multithreading and you don’t want conflicts in how the data behind that object is accessed. For example, you may be working with a database object, and if multiple threads each create their own database objects — all of which work with the same underlying data store — you could have a serious issue.

public class Database {
    private static Database singleObject;
    private int record;
    private String name;
    
    private Database(String s) {
        name = s;
        record = 0;
    }
    public static Database getInstance(String s) {
        /* Guard this with a mutex */
        if (singleObject == null){
            singleObject = new Database(s);
        }
        /* guard end */
        return singleObject;
    }
    .
    .
    .
}

subclassing a singleton is not a good idea — at best, the constructor will have to be made protected, which means various subclasses can work with i

Flyweight pattern

ch5

With the Flyweight pattern, you might also have only one object of a particular class — but it looks to your code as though it’s many different objects.

This pattern is called flyweight because instead of having to work with many massive, individual objects, you whittle them down to a smaller set of more generic objects, called flyweights, that can be configured at runtime to look like the more plentiful, massive objects.

A flyweight is a shared object that can be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context — it’s indistinguishable from an instance of the object that’s not shared.

You remove from those objects all the specialized, instance-specific contents that you can to end up with a shareable object, a flyweight, that acts like a template. That template object can then be configured at runtime by passing it all the specialized contents it needs to appear like one of the more massive objects.

Whenever you’ve got a large number of massive objects that are putting a strain on your application, the Flyweight pattern should pop into your mind.

To use the Flyweight pattern, you’ve got to keep track of the data you want to use to configure the flyweight to appear like various, more massive objects.

if you leave object creation up to the new operator, you might end up with multiple objects when you wanted only a single one, especially if you’ve got a multithreaded program.

public class TestFlyweight {
    public static void main(String args[]) {
        String names[] = {“Ralph”, “Alice”, “Sam”};
        int ids[] = {1001, 1002, 1003};
        int scores[] = {45, 55, 65};
        double total = 0;
        for (int loopIndex = 0; loopIndex < scores.length; loopIndex++){
            total += scores[loopIndex];
        }
        double averageScore = total / scores.length;
        Student student = new Student(averageScore);
        for (int loopIndex = 0; loopIndex < scores.length; loopIndex++){
            student.setName(names[loopIndex]);
            student.setId(ids[loopIndex]);
            student.setScore(scores[loopIndex]);
            System.out.println(“Name: “ + student.getName());
            System.out.println(“Standing: “ +
            Math.round(student.getStanding()));
            System.out.println(“”);
        }
    }
}

public class Student 
    String name;
    int id;
    int score;
    double averageScore;
    
    /* This has to be made a singleton and Thread safe, so private creator */
    public Student(double a) {
        averageScore = a;
    }
    public void setName(String n) {
        name = n;
    }
    public void setId(int i) {
        id = i;
    }
    public void setScore(int s) {
        score = s;
    }
    public String getName() {
        return name;
    }
    public int getID() {
        return id;
    }
    public int getScore() {
        return score;
    }
    public double getStanding() {
        return (((double) score) / averageScore - 1.0) * 100.0;
    }
}

Does the Flyweight pattern have any drawbacks? Some. The main issue is that it can take some time to configure a flyweight object, and if you’re always swapping configurations, you can lose much of the performance gains you hoped to achieve. Another possible drawback: Because you’re extracting a generic template class from your existing objects in order to create flyweight objects, you’re adding another layer of programming, which can make maintenance and extension harder.

Adapter pattern

ch6

Adapter pattern lets you "Convert the interface of a class into another interface the client expects. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces."

You use the Adapter design pattern when you’re trying to fit a square peg into a round hole. If what a class or object exposes isn’t what you need to end up with, you can add an adapter — much like an electrical outlet adapter for international travel — to give you what you need.

There’s another kind of adapter besides object adapters — class adapters. You explain to the company programmers: "While object adapters use composition to store the object they’re adapting, class adapters are designed to use multiple inheritance to merge the adapted class and the class you’re adapting it to."

One final note on adapters — besides adapting the behavior of a class or object, adapters can also improve that behavior by adding their own methods. For example, an adapted object that reports temperatures in Fahrenheit might be improved if its adapter also adds a method that reports temperatures in Centigrade.

Drawbacks of adapters? Not many — mostly that there’s an additional layer of code added, and so to maintain. But if rewriting legacy code isn’t an option, adapters provide a good option.

Facade design pattern

ch6

the Facade pattern gives you a wrapper that makes the original code easier to deal with.

The Facade design pattern makes an OOP interface (and that’s the general use of the term, not just a Java interface) easier to use. It’s fundamentally a design issue — if an object or class interface is too hard to work with, the Facade pattern gives you a front end to that interface to make it easier

the Facade pattern should "Provide a unified interface to a set of interfaces in a system. Facade defines a higher-level interface that makes the subsystem easier to use."

The idea is simple; a facade just simplifies the interface (using the generic sense of the word “interface” here, not a Java interface) between a class or object and the code that makes use of that class or object.

If one object needs to know too much about another to make their coupling loose, a Facade pattern can help.

Always go for the loosest coupling you can.

Template pattern

ch7

You use the Template Method design pattern when you’ve got an algorithm of several steps and you want to allow customization by subclasses. It’s that easy. Implement the steps in that algorithm as an overridable method calls in an abstract class, and let the subclasses override those steps as required.

the Template Method will "Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure." (Design Patterns: Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc. Publishing as Pearson Addison Wesley.)

So that means you should use the Template Method pattern when you have an algorithm that is made up of multiple steps, and you want to be able to customize some of those steps. Note that if you want to rewrite everything from scratch every time — if every step has to be customized by writing it from scratch — then you have no need of a template. Only if you have steps that are shared by various implementations of the algorithm do you need to work with a template.

you can write a method that defines a multi-step algorithm, just like the go method you’ve seen earlier in this chapter, which runs the multi-step algorithm corresponding to the robot’s work.

public void go() {
start();
getParts();
assemble();
test();
stop();
}

Then you make this method into a template by allowing subclasses to redefine (in Java terms, override) various steps in this algorithm as needed. In this case, to build the cookie robot, for example, you’d override the getParts, assemble, and test methods.

You can also provide hooks into your algorithm. A hook is a method that controls some aspect of that algorithm. For example, if you wanted to make the testing part of the Robot algorithm optional, you could surround that part with a conditional whose condition is set by a hook method named testOK.

public abstract class RobotHookTemplate {
  public final void go() {
    start();
    getParts();
    assemble();
    if (testOK()){
      test();
    }
    stop();
  }
...
  public boolean testOK() {
    return true;
  }
} 

By default, you can ignore the hook method testOK — if you do nothing with it, the Robot algorithm calls the full set of steps, including the test method. However, you can hook into the algorithm by overriding the testOK method in a subclass, like this in a new class, CookieHookRobot, where testOK returns false, not true.

Override the testOK in the subclass:

public boolean testOK() {
  return false;
}

Builder pattern

ch7

The GoF says that the Builder design patterns let you "Separate the construction of a complex object from its representation so that the same construction processes can create different representations."

Straight talk: A pattern that lets you separate and reuse a specific process to build something.(lynda.com) Only do thjis when construction process is complex.

The main difference between the Template Method and the Builder design patterns is in who creates the sequence of steps in the algorithm. In the Template Method, you do, and subclasses can make alterations. In the Builder pattern, the client code sets the sequence and number of steps in the algorithm and swaps between the builders you provide to create various objects that embody that algorithm.

Use the Builder design pattern when you want client code to have control over the construction process but want to be able to end up with different kinds of objects (each of which is built by a different type of builder).

  • Director:
    • Construct()
      • for all objects in structure {Builder->BuildPart()}
  • Builder:
    • BuildPart()
  • ConcreteBuilder
    • BuildPart()
    • GetResult()
  • Product

Itteration pattern

ch8

The Iterator pattern gives you a way of accessing the elements inside an object without having to know the internal details of that object.

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

In other words, iterators are designed to let you handle many different kinds of collections by accessing their members in a standard, accepted way, without having to know the internal details of those collections.

The Iterator design pattern is especially important when the collection you’re creating is made up internally of separate subcollections, as when you’ve mixed hashes with array lists, for example.

Iterators are usually written in Java as standalone classes. Why aren’t iterators built into the collections they work with? They could be, but in Java and other languages, they’re not. The design insight here is one of what’s called single responsibility — a class should have only one thing to do. The thinking is that the collection maintains the collection; the iterator provides access to the elements of the collection. Separating responsibilities between classes is useful when a class has to change — if there’s too much going on in a single class, it’s going to be too hard to rewrite. When change has to happen, a single class should have only one reason to change.

Composite pattern

ch8

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly

The Composite pattern is designed to let you handle different types of objects in the same collection in the same way, and iterators fit in naturally here — to handle the elements of a tree branch, for example, you can iterate over them.

The Composite pattern is all about creating tree-like structures where the leaves in a structure can be treated in the same way as the branches (which are substructures that can contain multiple leaves, as well as other branches). The idea here is that, to make life easier, you should be able to treat the leaves and compositions of leaves in a tree structure the same.

State pattern

Ch9.

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

Proxy pattern

Ch9.

Provide a surrogate or placeholder for another object to controll access to it.

Command pattern

Ch10

Mediator pattern

Ch10.

Abstract factory pattern

Ultimately a factory for other factories. A factory that creates other factories.(C# design patterns).

In a way, the Abstract Factory pattern describes a factory of factories, or, more properly thought of, an abstract specification for an actual object factory(Ch11).

An Abstract Factory is usually implemented as an abstract class that real, concrete factories extend. That unifies what the concrete factories do, while allowing leeway to fit differing requirements, as when you are using a different look and feel for each application(Ch11)

Bridge pattern

the “has-a” connection between the remote and the car type is called the bridge(Ch11)

TODO Is this something along the lines of Mediator?

The inspiration here is that when you have an abstraction that can vary, and that’s tied to an implementation that can also vary, you should decouple the two.

The GoF book says the Bridge design pattern should, “Decouple an abstraction from its implementation so that the two can vary independently.”

Circular Buffer design patterns

ch11 “A circular buffer is a memory allocation scheme where memory is reused (reclaimed) when an index, incremented modulo the buffer size, writes over a previously used location. A circular buffer makes a bounded queue when separate indices are used for inserting and removing data. The queue can be safely shared between threads (or processors) without further synchronization so long as one processor enqueues data and the other dequeues it.” —

Visitor pattern

A visitor object moves from object to object in the structure, visiting each one and capturing its state. When the visitor has captured all the data it needs from the objects in the structure it’s visiting, you can call the methods of the visitor, such as the fireable method that will return a list of personnel eligible for termination of employment.

The GoF book says the Visitor pattern should, “Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.”

Other design patterns

Model-view-control patterns

  • model - is the core component.
    • It represents knowledge.
    • It contains and manages the (business) logic, data, state, and rules of an application.
  • view - is a visual representation of the model.
    • Examples of views are a computer GUI, the text output of a computer terminal, a smartphone's application GUI, a PDF document, a pie chart, a bar chart, and so forth.
    • The view only displays the data; it doesn't handle it.
  • controller - is the link/glue between the model and view.
    • All communication between the model and the view happens through a controller.
  1. The user triggers a view by clicking (typing, touching, and so on) a button
  2. The view informs the controller of the user's action
  3. The controller processes user input and interacts with the model
  4. The model performs all the necessary validation and state changes and informs the controller about what should be done
  5. The controller instructs the view to update and display the output appropriately, following the instructions that are given by the model

Language specific Idioms

Computer specific Idioms

Organizational patterns

Process patterns

Analysis patterns

Clone this wiki locally