1. Design Patterns (initially 26 patterns, we will cover 6 of the core ones)
APIE
Abstraction (a specific guy or girl is a PERON class)
Polymorphism (different forms of implementations)
Inheritance (from super classes)
Encapsulation (grouping and protect)
2. The Strategy Pattern
3. The Adapter Pattern
4. The Obsever Pattern
5. The Decorator Pattern
6. The Iterator Pattern
Similar to 36计
Design principles
E.g. Encapsulate What Varies
SOLID
Problems in inheritance
E.g. Duck simulator
The over use of inheritance could cause issue when the super class has too many thing that the subclass don’t have. (Every time a not so fit subclass is created, the subclass will need to rewrite all the not so fit methods.
What about interface? (More implementation and flexible)
Issues: Imagine that every new type of Duck will need to reimplement the interface again.
No code reuse now...
Solution
🚩 Encapsulate what varies
Separate what vary: Move the quack()
and fly()
methods out of the original super class, and make then two interfaces
🚩 Program to an Interface, Not an Implementation
The right way to use an interface:
Then implement their possible variations
QuackBehavior interface
Quack
Squeak
Mute
FlyBehavior
FlyWithWings
FlyNoWay
💚 The Strategy Pattern
This patten defines a family of algorithms, encapsulates each one, and makes them interchangeable. This lets the algorithm vary independently from clients that use it.
Composition: HAS-A is Better Than IS-A
🚩 Favor Composition over Inheritance
❤ Challenge
Building a Class for Phone CamerApp and the subclasses: Basic CameraApp, and CameraPlusApp
public abstract class PhoneCamreaApp {
ShareBehavior
}
public class BasicCameraApp extends PhoneCameraApp {
edit();
}
public class CameraPlusApp extends PhoneCameraApp {
public CameraPlusApp {
}
@Override
public void edit() {
System.out.println(“Edit type 1”);
}
}
Usage: When you have a class, and your vendor use another class that are similar but not the same.
Building an adaptor class that implements your interface, and then the vendor's class still works as normal. At the point of test (using the functions that run on your own implements), you just need to feed the adaptor with the vendor class, and then it will spit out a class that implements your interface.
Interfaces: Subject and Observer
Classes: SimpleSubject, Observer1, Observer2, ...
Once the value of subject is updated, the update function of each observer class implements the Observer
interface will then update the value of all.
💚 The Open-Closed Principle
Classe should be open for extensions, but closed for modifications.
6. The Iterator Pattern
Using iterators (normally built-in)
7. The Factory Pattern
Preventing the opening of the super class, and moving the updating function to the factory. For example, in the Calendar class, we will need to pick a zone. The zone could be updated (when we need to add new zones), thus we can have a ZoneFactory class and a createZone function in it, which will return the specific Zone subclass (inherited the Zone class).
So this can prevent any opening for the update (add or remove) for the new Zone types. And we just need to update the ZoneFactory class to edit this.
Any subclass of Calendar can easily change the constructor, which get a ZoneFactory class argument, to declare the type of Zone subclass they need.
⭐ Simplicity is the main reason we use these patterns, so if you feel that a hypothesis program can be simpler without pattern, then don't make it complicated with pattern.
Define
Strategy pattern move out the changing functionalities from super class, and then make each functionality an interface. Implement each of the interfaces to different concrete methods.
Solves the modification of superclass all the time by creating interfaces to changing functions in superclass, and implement different methods for the change functionality interface.
You can add a function in the super class object to update the specific kind of functionality interface implements you want to pick
Usage
- Extracting the changing functionality
2 .Make an interface that have an abstract function that is the same as the one in the super class that always changes
- Implementing the functionality interface for all existing functions
- In the super class, save a global interface variable for the functionality.
- Assigning it either (1) within the subclass constructor, or (2) a setting function that take the functionality interface implementations and update the global behaviors variables
- The behavior then can be used in a normally share function in the superclass without needing to change for any of the update, delete, add
Define
The adaptor pattern creates a class that implements the target object interface type, and take the original interface object type for it’s constructor. In the concrete adaptor class, all the interface function for the target class are implemented based on the corresponding original class class functions. Aldo the original given class object is stored as a private global object in the Adaptor.
Solves issues
So that for any test or simulator that requires a specific type of class, can consume the similar object that implements different type of interface.
Principles
Dependency Inversion Principle (DIP): directly implement the interface/abstract class, and not the concrete class.
Open-closed Principle (OCP): open for extension and closed for modification of the super class
Interface Segregation Principle (ISP): interfaces are sparated
Single Responsibility Principle (SRP): each interface only responsible for one type of object.
Usage
Why use it
For functions that need a specific type of interface object, but you only have a different original interface object.
The Adaptor Pattern can help you here by:
Building an adaptor class that implements the target interface, and take the original interface object in the constructor. The adaptor will implement all the target interface functions with the desired behaviors based on the original interface object has. After calling to make a new adaptor with the original interface object feeder in the constructor of it, you will get an object that can be used for any function that required the target interface object.
Steps
- You first have two interfaces and two classes that implements them
- You have a function that only consume/take type1 interface, so you want to convert the type2 interface object to type1 in someway (hint: adaptor)
- The adaptor is (type2 -> type1), and implements type1 interface and all its functions. And the constructor of the adaptor takes a type2 interface object (saved globally for usage)
- The adaptor functions will use the function of type2 interface object, and named after the type1 interface (in any legal logics)
Define
The Observer Pattern allows one subject class to update the values stored in all the observer objects (can be any value for each update function).
Solve problem...
Now with the observer pattern, you can easily update value of a group of observer classes from one subject, as long as they implements the update()
functions in the expected way.
Steps
- Building interfaces for Subject and Observer. Subjects need to implement
registerObserver(Observer)
,removeObserver(Observer)
, andupdateValue(val)
. And the Observers need to implement theupdate(val)
function.
- Store the value as private global in both Subject and Observers. And the Observer will save the subject in calling the constructor with the corresponding subject.
- The
registerObserver(this)
is called in the Observer constructor
- The
Observer.update()
will be called in theSubject.updateValue(val)
in the subject class object.
Define
The decorator pattern can help you to add decorator elements by inserting current object into the new decorator object, and append new features to it.
Steps
- You will need a main abstract class for the main objects, and implement them before appending any elements
- The decorator class will extends the main class, and add abstract functions for functionality modification (even the previously implemented functions are fine).
- When you want to add the decorators, just create a new decorator object, and with the main class as an argument to the constructor of it.
- You can save the decorator object in the main abstract class type, because the decorator extends from the abstract class that extends the main abstract class.
For each loop is a good example
Define
The Factory Pattern can delay and encapsulate the creation of concrete class types until the point of Runtime (constructor call). It also made sure that all the concrete class are not directly created by other object, but only by calling the corresponding object id saved in the Factory, and got a new object created and returned from there. (Any class that use the factory pattern to create utility objects will take a factory in the constructor, and feed that with the correct id of the object).
Steps
- Creating a Superclass/Interface for the utility objects, and creating the Superclass/Interface for the main objects
- Create subclasses that extends the superclasses for the utility objects with each different variations.
- Create a Factory that have a
createObject(String id)
function, that takes an id and return a corresponding utility object created here.
- In the main subclass constructor, you take a factory object, and call it with an id that you want for the utility object. Then you will save the object (protected from super class) in the main subclass and use it’s features.
Creator patterns
The factory method patterns
Multiple factories for different styles and same type or category of product. Similar API for each product
The abstract factory patten
Similar to the factory method pattern, but here we create a family of product with each one of them assign the method we all create/have by the factories. Similar set of product for each instance created
Builder Patter
Using director to include the constructive logic to build with builder interface concrete class to build class (product)
Prototype Pattern
Copy the complex
setup that already created and then build a copied instance based on that! Just implement Clone method.
Singleton Patten
The key is to make sure that there is only one class (static) created and can be accessible with a static method from the “class variable” that is globally accessible.
this can prevent multithread issue by dB le assign seats. The constructor must be private.
Trick to create the singleton class is to make
the constructor to be private and instantiate it from the getInstance() method. The instantiation is only ran when the global Single class reference is null.