Skip to content

Instantly share code, notes, and snippets.

@mdgriffin
Last active August 14, 2022 14:46
Show Gist options
  • Save mdgriffin/4f07ed68790cd2aa8d5bfd9f969153d7 to your computer and use it in GitHub Desktop.
Save mdgriffin/4f07ed68790cd2aa8d5bfd9f969153d7 to your computer and use it in GitHub Desktop.

Design Patterns Notes

Links

Pattern Classification

Patterns Scopes

From GoF Design Patterns Book:

Scope, specifies whether the pattern applies primarily to classes or to objects. Class patterns deal with relationships between classes and their subclasses. These relationships are established through inheritance, so they are static—fixed at compile-time. Object patterns deal with object relationships, which can be changed at run-time and are more dynamic. Almost all patterns use inheritance to some extent. So the only patterns labeled "class patterns" are those that focus on class relationships. Note that most patterns are in the Object scope.

Class and Object patterns

Pattern Purposes

From GoF Design patterns book:

Purpose, reflects what a pattern does. Patterns can have either creational, structural, or behavioral purpose. Creational patterns concern the process of object creation. Structural patterns deal with the composition of classes or objects. Behavioral patterns characterize the ways in which classes or objects interact and distribute responsibility.

  • Creational patterns
    • Abstract Factory
      • Creates an instance of several families of classes
    • Builder
      • Separates object construction from its representation
    • Factory Method
      • Creates an instance of several derived classes
    • Singleton
      • Ensure a class has only one instance, and provide a global point of access to it.
  • Structural Patterns
    • Adapter
      • Match interfaces of different classes
    • Decorator
      • Add responsibilities to objects dynamically
  • Behavioural Patterns
    • Command
      • Encapsulate a command request as an object
    • Observer
      • A way of notifying change to a number of classes
    • State
      • Alter an object's behavior when its state changes
    • Strategy
      • Encapsulates an algorithm inside a class
    • Template method
      • Defer the exact steps of an algorithm to a subclass

Solid Principles

  • Single Responsibility: A class should have one and only one reason to change, meaning that a class should have only one job.
  • Open/Closed: Objects or entities should be open for extension, but closed for modification.
  • Liskov Substitution: Every subclass/derived class should be substitutable for their base/parent class. That is use the most generic type possible.
  • Inteface Segregration: A client should never be forced to implement an interface that it doesn't use or clients shouldn't be forced to depend on methods they do not use.
  • Dependency Inversion: Entities must depend on abstractions not on concretions. It states that the high level module must not depend on the low level module, but they should depend on abstractions.

Liskov Substitution

  • LSP states that a subtypes must be substitutable for their base (super) types.
  • Related to refused bequest
  • Liskov's principle imposes some standard requirements on signatures

Covariant

  • Arrays are said to be covariant which basically means that, given the subtyping rules of Java, an array of type T[] may contain elements of type T or any subtype of T. For instance
class Super {
  Object getSomething(){}
}
class Sub extends Super {
  String getSomething() {}
}

Sub#getSomething is covariant because it returns a subclass of the return type of Super#getSomething (but fullfills the contract of Super.getSomething())

Contravariants

class Super{
  void doSomething(String parameter)
}
class Sub extends Super{
  void doSomething(Object parameter)
}

Sub#doSomething is contravariant because it takes a parameter of a superclass of the parameter of Super#doSomething (but, again, fullfills the contract of Super#doSomething)

Notice: this example doesn't work in Java. The Java compiler would overload and not override the doSomething()-Method. Other languages do support this style of contravariance.

Behavioural conditions

  • Preconditions cannot be strengthened in a subtype.
    • A condition that must be true prior to the execution of som block of code
    • Cannot be strengthened in a subtype
    • If a class called Authenticate checks password, the superclass needs a 10 character password, the subclass should then not look for a 12 character password A client uses a base class whose calculateShippingCost() mehthod does not require a destination reference. A subclass that introduces a precondition that a destination must be specified causes the client to breaks.
  • Postconditions cannot be weakened in a subtype.
    • A condition that must be true after the execution of some block of code
    • Cannot be weakened in a subtype A client that relies on the return value of calculateShippingCost() being non-zero breaks when a subclass, under special circumstances, allows themethod to return a zero value.
  • Invariants of the supertype must be preserved in a subtype.
    • Things that are always true throughout the execution of the code
    • Invariants must persist in the subtype
  • History constraint (the "history rule"): Objects are regarded as being modifiable only through their methods (encapsulation). Because subtypes may introduce methods that are not present in the supertype, the introduction of these methods may allow state changes in the subtype that are not permissible in the supertype. T

Other Principles

  • YAGNI: You aren't going to need it
  • Inversion of Control / Hollywood principle
    • Don't call us, we'll call you
    • Techniques
      • Using a factory (NB: this is more general than the GoF's Factory Method pattern)
      • Using a service locator pattern (we didn't cover this
      • Using Dependency Injection (DI), for example:
      • A constructor injection
      • Setter injection
      • Using a contextualized lookup (we didn't cover this)
      • Using template method design pattern
      • Using strategy design pattern
  • DRY: Don't repeat yourself
    • Repeating codes means changes require making changes in multiple places
  • CVA: Commonality and Variance Analysis - distinguish parts that change from parts that don't and make it easy and safe for the change to happen Encapsulate change
    • CVA attempts to identify the commonalities (generic concepts) and variations (concrete implementations) in a problem domain
    • Such analysis will produce:
      • abstract base classes that can be used to interface with
      • concrete implementations in a generic way that will enable abstraction, type encapsulation, and polymorphism
  • Depend in the direction of stability
    • Identity what changes frequently from what doesn't.
    • This could also mean identify that is hard to change from what is easy to change
    • It may be easy to change the UI, but hard to change underlying services/APIs
    • The underlying JDK is stable for instance
  • Program to an interface, not an implementation
    • Programming to a constraint, the interface being the constraint
    • A level abstratcion away from concrete classes
    • Does not have to be an interface, could also be an interface or a class used in an abstract way.
  • Favour composition over inheritance
  • Principle of Least Knowledge (Don't talk to strangers)
  • Strive for high cohesion
  • Strive for low coupling

Code Smells

Links

Smells

  • Bloaters:
    • Long Methods:
    • Large Class
    • Primitive Obsession
    • Long Parameter List
    • DataClumps
  • Object-Orientation Abusers:
    • Refused Bequest: If a subclass uses only some of the methods and properties inherited from its parents, the hierarchy is off-kilter. The unneeded methods may simply go unused or be redefined and give off exceptions.
    • Switch Statements
    • Temporary Field
    • Alternative Classes with Different Interfaces
  • The Change Preventers:
    • Shotgun Sugery: Making a change requires changing a number of different classes
    • Divergent Change
    • Parallel Inheritance Hierarchies
  • The Dispensables:
    • Lazy class
    • Data class
    • Duplicate Code
    • Dead Code
    • Speculative Generality
  • The Couplers:
    • Feature Envy
    • Inappropriate Intimacy
    • Message Chains
    • Middle Man

Patterns that use inheritance

An important design rule is to favour composition over inheritance. Most of the patterns follow this principle, however a few patterns use inheritance.

  • Adapter
  • Template Method
  • Factory Method

Structural

  • Deals with composition and relations.

Adapter

  • Also known as a wrapper
  • Think power adapters when going on holiday
  • Adapter pattern lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class.
  • Object Adapter: uses composition and can wrap classes or interfaces, or both. It can do this since it contains, as a private, encapsulated member, the class or interface object instance it wraps.
  • Class Adapter: uses inheritance and can only wrap a class. It cannot wrap an interface since by definition it must derive from some base class.
    • Class Adaption is not possible in Java because it require multiple inheritance
    • Both the adaptee and the target classes are extended

Intent

Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

Examples in JDK

‐ java.util.Arrays#asList() ‐ javax.swing.JTable(TableModel) ‐ java.io.InputStreamReader(InputStream) ‐ java.io.OutputStreamWriter(OutputStream) ‐ javax.xml.bind.annotation.adapters.XmlAdapter#marshal() ‐ javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()

Class Adapters versus Object Adapters

Example

public class GermanElectricalSocket {

    public void plugIn(GermanPlugConnector plug) {
        plug.giveElectricity();
    }
}

public interface GermanPlugConnector {

    public void giveElectricity();
}

public class UKElectricalSocket {

    public void plugIn(UKPlugConnector plug) {
        plug.provideElectricity();
    }
}

public interface UKPlugConnector {

    public void provideElectricity();
}

public class GermanToUKPlugConnectorAdapter implements UKPlugConnector {

    private GermanPlugConnector plug;

    public GermanToUKAdapter(GermanPlugConnector plug) {
        this.plug = plug;
    }

    @Override
    public void provideElectricity() {
        plug.giveElectricity();
    }

}

Usage:

GermanPlugConnector plugConnector = //.. create a GermanPlugConnector
UKElectricalSocket electricalSocket = new UKElectricalSocket();
UKPlugConnector ukAdapter = new GermanToUKAdapter(plugConnector);
electricalSocket.plugIn(ukAdapter);

Decorator:

Intent

  • Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
  • Client-specified embellishment of a core object by recursively wrapping it.
  • Can be seen when creating a type passes in the same type.

Usage in the JDK

This is actually used all over the JDK, the more you look the more you find, so the list below is definitely not complete.

‐ java.io.BufferedInputStream(InputStream) ‐ java.io.DataInputStream(InputStream) ‐ java.io.BufferedOutputStream(OutputStream) ‐ java.util.zip.ZipOutputStream(OutputStream) ‐ java.io.BufferedFileReader(FileReader) ‐ java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap

Examples

Input Stream

  • InputStream is an abstract class. Most concrete implementations like BufferedInputStream, GzipInputStream, ObjectInputStream, etc. have a constructor that takes an instance of the same abstract class. That's the recognition key of the decorator pattern (this also applies to constructors taking an instance of the same interface).
    • LineNumberInputStream(BufferedInputStream(FIleInputStream)))
    • Buffered Input stream accepts an input stream to it's constructor
    • The LineNumberInputStream also accepts an input stream that provides the added functionality of keeping track of the current line number.

Greeter

public interface Greeter {
    String getMessageOfTheDay();
}
public class BasicGreeter implements Greeter {

    @Override
    public String getMessageOfTheDay() {
        return "Welcome to my server";
    }

}
public abstract class GreeterDecorator implements Greeter {

    protected Greeter greeter;

    public GreeterDecorator(Greeter greeter) {
        this.greeter = greeter;
    }

    public String getMessageOfTheDay() {
        return greeter.getMessageOfTheDay();
    }

}
public class StrangerDecorator extends GreeterDecorator {

    public StrangerDecorator(Greeter greeter) {
        super(greeter);
    }

    @Override
    public String getMessageOfTheDay() {
        return "Hello Stranger " + super.getMessageOfTheDay();
    }

}
public class UserDecorator extends GreeterDecorator {

    private String username;

    public UserDecorator(Greeter greeter, String username) {
        super(greeter);
        setUsername(username);
    }

    @Override
    public String getMessageOfTheDay() {
        return "Hello " + getUsername() + " " + super.getMessageOfTheDay();
    }

    /// ... get/set

}

Usage:

Greeter greeter = new BasicGreeter();
System.out.println(greeter.getMessageOfTheDay());

Greeter strangerGreeter = new StrangerDecorator(greeter);
System.out.println(strangerGreeter.getMessageOfTheDay());

Greeter evenStrangerDecrorator = new StrangerDecorator(new StrangerDecorato(greeter));
System.out.println(evenStrangerDecrorator.getMessageOfTheDay());

Greeter userGreeter = new UserDecorator(greeter, "Tom Tim");
System.out.println(userGreeter.getMessageOfTheDay());

Creational

  • Deals with the instanation of objects

Abstract Factory:

  • It enables one to decouple an application from the concrete implementation of an entire framework one is using.
  • This is also found all over the JDK and a lot of frameworks like Spring. They are simple to spot, any method that is used to create an object but still returns a interface or abstract class.

Intent

  • Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
  • A hierarchy that encapsulates: many possible "platforms", and the construction of a suite of "products".
  • The new operator considered harmful.

Examples in the JDK

‐ java.util.Calendar#getInstance() ‐ java.util.Arrays#asList() ‐ java.util.ResourceBundle#getBundle() ‐ java.sql.DriverManager#getConnection() ‐ java.sql.Connection#createStatement() ‐ java.sql.Statement#executeQuery() ‐ java.text.NumberFormat#getInstance() ‐ javax.xml.transform.TransformerFactory#newInstance()

Example

Example 1

class A {
    private Factory factory;

    public A(Factory factory) {
        this.factory = factory;
    }

    public void doSomething() {
        //The concrete class of "f" depends on the concrete class
        //of the factory passed into the constructor. If you provide a
        //different factory, you get a different Foo object.
        Foo f = factory.makeFoo();
        f.whatever();
    }
}

interface Factory {
    Foo makeFoo();
    Bar makeBar();
    Aycufcn makeAmbiguousYetCommonlyUsedFakeClassName();
}

//need to make concrete factories that implement the "Factory" interface here

Example 2

public interface PizzaIngredientFactory {
    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClam();
}

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {

    public Dough createDough() {
        return new ThinCrustDough();
    }
    
    public Sauce createSauce() {
        return new MarinaraSauce();
    }
    
    public Cheese createCheese() {
        return new ReggianoCheese();
    }
    
    public Veggies[] createVeggies() {
        Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
        return veggies;
    }
    
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }
    
    public Clams createClam() {
        return new FreshClams();
    }

}

Builder:

Used simplify complex object creation by defining a class whose purpose is to build instances of another class. The builder pattern also allows for the implementation of a Fluent Interface.

‐ java.lang.StringBuilder#append() ‐ java.lang.StringBuffer#append() ‐ java.sql.PreparedStatement ‐ javax.swing.GroupLayout.Group#addComponent()

Factory method:

Simply a method that returns an actual type.

Factory Method

Intent

  • Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
  • Defining a "virtual" constructor.
  • The new operator considered harmful.

Usages in JDK

  • java.util.Calendar getInstance method
  • java.lang.Proxy#newProxyInstance()
  • java.lang.Object#toString()
  • java.lang.Class#newInstance()
  • java.lang.reflect.Array#newInstance()
  • java.lang.reflect.Constructor#newInstance()
  • java.lang.Boolean#valueOf(String)
  • java.lang.Class#forName()

Example

Basic Example

class Creator {

    abstract Foo makeFoo();

    // ... other methods
}

class ConcreteCreator extends Creator {
    @Overrides
    public Foo makeFoo() {
        //subclass is overriding the factory method 
        //to return something different
        return new SpecialFoo();
    }
}

Interface example

public interface Blacksmith {
    Weapon manufactureWeapon(WeaponType weaponType);
}

public class ElfBlacksmith implements Blacksmith {
    public Weapon manufactureWeapon(WeaponType weaponType) {
        return new ElfWeapon(weaponType);
    }
}

public class OrcBlacksmith implements Blacksmith {
    public Weapon manufactureWeapon(WeaponType weaponType) {
        return new OrcWeapon(weaponType);
    }
}

Singleton:

  • Used in other design patterns like Abstract Factory, Builder, Prototype, Facade

Intent

  • Ensure a class has only one instance, and provide a global point of access to it.
  • Encapsulated "just-in-time initialization" or "initialization on first use".

Examples in JVM

I didn't find an example but another solution would be to use an Enum as Joshua Bloch suggests in Effective Java.

‐ java.lang.Runtime#getRuntime() ‐ java.awt.Toolkit#getDefaultToolkit() ‐ java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()
‐ java.awt.Desktop#getDesktop()

Thead Safe Singleton using Lazy Instantiation

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    
    private ThreadSafeSingleton(){}
    
    public static synchronized ThreadSafeSingleton getInstance(){
        if(instance == null){
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
    
}

Using Enums for Singleton

public enum SingletonEnum {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public void setValue(int value) {
        this.value = value;
    }
}

Usage

public class EnumDemo {
    public static void main(String[] args) {
        SingletonEnum singleton = SingletonEnum.INSTANCE;
        System.out.println(singleton.getValue());
        singleton.setValue(2);
        System.out.println(singleton.getValue());
    }
}

Behavioural

  • Deals with responsibilities and communication between objects

Command:

  • OOP replacement for callback functions
  • To wrap a command in an object so that it can be stored, passed into methods, and returned like any other object.

Intent

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

Usage in JDK

‐ java.lang.Runnable ‐ javax.swing.Action

Examples

  • Runnable and Swing's ActionListener are examples in the JDK, ActionListener is an interface which simply defines a actionPerformed(ActionEvent e) method that will get called when some action is performed, like mouse click.
  • A programmable remote control where each of the buttons can be programmed to execute a different function. Each button action would need to implement an Action interface

Participants

  • Command
    • declares an interface for executing an operation.
  • ConcreteCommand (PasteCommand, OpenCommand)
    • defines a binding between a Receiver object and an action.
    • implements Execute by invoking the corresponding operation(s) on Receiver.
  • Client (Application)
    • creates a ConcreteCommand object and sets its receiver.
  • Invoker (MenuItem)
    • asks the command to carry out the request.
  • Receiver (Document, Application)
    • knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver.

Light On/Off Example

Command:

public interface Command{
  public void execute();
}

Concrete Command:

public class LightOnCommand implements Command{
  //reference to the light
  Light light;
  public LightOnCommand(Light light){
    this.light = light;
  }
  public void execute(){
    light.switchOn();
  }
}

Concrete Command:

public class LightOffCommand implements Command{
  //reference to the light
  Light light;
  public LightOffCommand(Light light){
    this.light = light;
  }
  public void execute(){
    light.switchOff();
  }
}

Receiver:

public class Light{
  private boolean on;
  public void switchOn(){
    on = true;
  }
  public void switchOff(){
    on = false;
  }
}

Invoker:

public class RemoteControl{
  private Command command;
  public void setCommand(Command command){
    this.command = command;
  }
  public void pressButton(){
    command.execute();
  }
}

Client:

public class Client{
  public static void main(String[] args)    {
    RemoteControl control = new RemoteControl();
    Light light = new Light();
    Command lightsOn = new LightsOnCommand(light);
    Command lightsOff = new LightsOffCommand(light);
    //switch on
    control.setCommand(lightsOn);
    control.pressButton();
    //switch off
    control.setCommand(lightsOff);
    control.pressButton();
  }
}

Observer:

Used to provide a way for a component to flexibly broadcast messages to interested receivers.

Intent

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Usage in the JDK

‐ java.util.EventListener ‐ javax.servlet.http.HttpSessionBindingListener ‐ javax.servlet.http.HttpSessionAttributeListener ‐ javax.faces.event.PhaseListener

Questions

  1. Write an application that uses the observer pattern. This example should have multiple subjects and multiple observers. What issues arise?
  2. We need to use the Observer pattern in a multi-threaded environment, what design issues do we need to consider and how might we solve them. Use code to explore solutions and problems
  3. Why does Java's Swing framework provide the following method: SwingUtilities.invokeandwait(). (HINT: look at the source code and google for answers)
  4. What issues do we need to consider for dynamically adding and removing listeners.
  5. Why might the Mediator pattern be useful when using the Observer pattern, give an example in code
  6. When might you consider using the Vector vs ArrayList collections for an implementation of the Observer pattern
  7. Discuss how you would decide on a push vs pull model for the Observer pattern
  8. Why is Java's implementation of the Observer pattern a poor one?
  9. Where in the JDK is the observer pattern used, what design decisions were considered and allowed for in the implementation.
  10. Discuss the role of the Observer in the MVC pattern
  11. How does the idea of notification work in an n-tier system where the principle "Code in the direction of Stability" should/must be considered?
  12. Explain how the Observer pattern might be used when trying to keep multiple views notified of changes to data in a database

Example

Weather station that maintains a list of observers, and then passes the current weather information to any observers when the weather changes or at set intervals. The observers can then do what they like with the information, like display it in a UI

State:

This allows you easily change an object’s behavior at runtime based on internal state.

‐ java.util.Iterator ‐ javax.faces.lifecycle.LifeCycle#execute()

Strategy:

  • Strategy pattern is also known as Policy Pattern.
  • One of the best example of strategy pattern is Collections.sort() method that takes Comparator parameter. Based on the different implementations of Comparator interfaces, the Objects are getting sorted in different ways.
  • Is intended to provide a means to define a family of algorithms, encapsulate each one as an object.
  • These can then be flexibly passed in to change the functionality.

Intent

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

Usage in the JDK

‐ java.util.Comparator#compare() ‐ javax.servlet.http.HttpServlet ‐ javax.servlet.Filter#doFilter()

Java 8 and the Strategy Pattern

Addition of lambdas allows the staregy to be defined in a lambda.

Example

See shopping cart example here. Various payment strategies can be passed in like PaypalPaymentStrategy and CreditCardPaymentStrategy can be passed in to handle the various methods of payment.

Template method:

Allows subclasses to override parts of the method without rewriting it, also allows you to control which operations subclasses are required to override.

‐ java.util.Collections#sort() ‐ java.io.InputStream#skip()
‐ java.io.InputStream#read()
‐ java.util.AbstractList#indexOf()

Intent

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.

UML Example

UML Example of Template Method

Example

public abstract class CaffeineBeverage {
  
    final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}
 
	abstract void brew();
  
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println("Boiling water");
	}
  
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
}

public class Coffee extends CaffeineBeverage {
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
}

public class Tea extends CaffeineBeverage {
	public void brew() {
		System.out.println("Steeping the tea");
	}
	public void addCondiments() {
		System.out.println("Adding Lemon");
	}
}

Example with a Hook

From the Head First Book: A hook is a method that is declared in the provides a default implementation. abstract class, but only given an empty or default implementation. This gives subclasses the ability to “hook into” the algorithm at various points, if they wish; a subclass is also free to ignore the hook.

public abstract class CaffeineBeverageWithHook {
 
	final void prepareRecipe() {
		boilWater();
		brew();
		pourInCup();
		if (customerWantsCondiments()) {
			addCondiments();
		}
	}
 
	abstract void brew();
 
	abstract void addCondiments();
 
	void boilWater() {
		System.out.println("Boiling water");
	}
 
	void pourInCup() {
		System.out.println("Pouring into cup");
	}
 
	boolean customerWantsCondiments() {
		return true;
	}
}

public class CoffeeWithHook extends CaffeineBeverageWithHook {
 
	public void brew() {
		System.out.println("Dripping Coffee through filter");
	}
 
	public void addCondiments() {
		System.out.println("Adding Sugar and Milk");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
}

public class TeaWithHook extends CaffeineBeverageWithHook {
 
	public void brew() {
		System.out.println("Steeping the tea");
	}
 
	public void addCondiments() {
		System.out.println("Adding Lemon");
	}
 
	public boolean customerWantsCondiments() {

		String answer = getUserInput();

		if (answer.toLowerCase().startsWith("y")) {
			return true;
		} else {
			return false;
		}
	}
 
	private String getUserInput() {
		// get the user's response
		String answer = null;

		System.out.print("Would you like lemon with your tea (y/n)? ");

		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			answer = in.readLine();
		} catch (IOException ioe) {
			System.err.println("IO error trying to read your answer");
		}
		if (answer == null) {
			return "no";
		}
		return answer;
	}
}

Abstract factory vs factory method

Source: https://stackoverflow.com/questions/5739611/differences-between-abstract-factory-pattern-and-factory-method

The main difference between a "factory method" and an "abstract factory" is that the factory method is a single method, and an abstract factory is an object. I think a lot of people get these two terms confused, and start using them interchangeably. I remember that I had a hard time finding exactly what the difference was when I learnt them.

Because the factory method is just a method, it can be overridden in a subclass, hence the second half of your quote:

the Factory Method pattern uses inheritance and relies on a subclass to handle the desired object instantiation.

The quote assumes that an object is calling its own factory method here. Therefore the only thing that could change the return value would be a subclass.

The abstract factory is an object that has multiple factory methods on it. Looking at the first half of your quote:

with the Abstract Factory pattern, a class delegates the responsibility of object instantiation to another object via composition ...

What they're saying is that there is an object A, who wants to make a Foo object. Instead of making the Foo object itself (e.g., with a factory method), it's going to get a different object (the abstract factory) to create the Foo object.

Other

Typed vs Statically Typed

Typed

Statically typed, before using a variable, a variable must be tagged with a type

Streamlined Object Modelling

  • 4 things that you model
    • People
      • Who are you?
      • Actor
      • Role
    • Places
      • Where are you?
      • Place
      • Outerplace
    • Things
      • What are you?
      • Item & Specific Item
      • Assembly & Part
      • Container & Part
      • Group & Member
    • Events
      • Something that happens at a time
      • Over a duration
      • In a sequence
      • Transaction
      • Composite transaction & line item
      • Follow-up transaction

Java Access Levels

Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N
  • You make a method protected when you want subclasses in any package access to the method.

UML Notation

UML Cheat Sheet

5 Tests for inheritance

Consider the advice given by Peter Coad on inheritance:

Inheritance is used to extend attributes and methods but encapsulation is weak within a class hierarchy, so use of this mechanism is limited. Use it when you can satisfy the following criteria:

  1. "Is a special kind of" not "is a role played by"
  2. Never needs to transmute to be an object in some other class
  3. Extends rather than overrides or nullifies superclass
  4. Does not subclass what is merely a utility class (useful functionality you'd like to resuse)
  5. Within problem domain expresses special kinds of:
    • roles,
    • transactions, or
    • things

Example 1: Should a class Person be extended by classes TravelAgent and Passenger?

  • Rule #1: Fail, both are roles not special types of
  • Rule #2: Fail, A TravelAgent can be a passenger and vice versa
  • Rule #3: Pass
  • Rule #4: Pass
  • Rule #5: Fail. A design using inheritance eould not express a kind or role, transaction, or thing

Example 2: Should a Transaction class be extended by Reservation and Purchase classes?

NOTE: A transaction is something that happens at a particular time, or over some duration Reservation and Purchase would therefore be classified as Transactions.

  • Rule #1: Pass, both are "special kinds of" transactions
  • Rule #2: Pass, neither transmutes
  • Rule #3: Pass, they both extend a basic transaction
  • Rule #4: Pass.
  • Rule #5: Pass, In the design, both would express special kinds of

Example 3: Can we extend Sensor with Remote Sensor?

  • Rule #1: Pass, RemoteSensor is a special kind of
  • Rule #2: Pass, never transmutes
  • Rule #3: Fail, RemoteSensor nullkifies. It has no use for the the association to ProblemInterval, and has no use for the three methods: activate(), monitor, access()
  • Rule #4: Pass.
  • Rule #5: Pass, Here, it's a special kind of thing.

So, inheritance fails on one of the 5 tests. We can fix this with a redesign.. See example3_fixed.puml. This extracts the problematic code from Sensor into a new subclass. Now, All five tests pass

Distinction between interface inheritance and implementation inheritance

From http://whats-in-a-game.com/implementation-inheritance-is-evil/:

  • Interface inheritance: Derived classes share the public method interface of the class and override its implementation. Java/C# makes this clear by having “Interfaces” where the methods in the Interface has no implementation. Interface inheritance is the good type of inheritance, required for polymorphism – the ultimate tool for creating extensible code in Object-Oriented Programming.
  • Implementation inheritance: Derived classes share the data and implementation of methods in the base class. This leads to inflexible systems where derived classes couple tightly to their base classes. From now on, when I say inheritance, this is the type of inheritance I’m referencing.

Problems with implementation inheritance:

  • Inheritance relationships reduce extensibility. Derived classes are tightly coupled to your base class keeping you from trading out behaviors from your base classes with different ones and requiring you to make different classes to mix and match different base classes. Additionally, in most languages, this tightly coupled relationship can’t be changed at runtime, which composition would allow you to do.
  • Every class that inherits from another class diminishes readability. It’s hard to know where functions are declared and what classes override certain methods.
  • You can’t test the derived class separate from the base class. This impact on testability is a dead giveaway that your code is tightly coupled. Remember, testable code is extensible code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment