Writing in the front
To judge whether a programmer is good, show me the code. Excellent code readability, high cohesion low coupling, extensible. There is no other way to write good code and be a good programmer than to look at open source frameworks written by Daniel, learn the best of them, and learn more about design patterns.
Design pattern can be divided into three types: creation pattern, structure pattern and behavior pattern.
Factory Method Pattern
Define an interface for creating objects, and let the class that implements the interface decide which class to instantiate. The factory method lets the instantiation of a class be deferred to a subclass, which is the creation pattern.
A factory object typically contains one or more methods for creating the various types of objects that the factory can create. These methods may take parameters that specify how the object was created, and eventually return the created object.
A factory is usually an object used to create other objects. A factory is an abstraction of a constructor used to implement different allocation schemes.
An example of the Wikipedia factory method
Public interface Button{} public interface Button{inButton
public class WinMacButton implements Button{} public class MacButton implements Button{ interface ButtonFactory { Button createButton(); } // actually create WinButton implementation class, implementation ButtonFactory public class WinButtonFactory implements ButtonFactory {
@Override
public static Button createButton() {return new WinButton(); }} // Create the MacButton implementation class, MacButtonFactory public class implements ButtonFactory {@override public static ButtoncreateButton() {returnnew MacButton(); }}Copy the code
Abstract Factory Pattern
Encapsulate a set of separate factories with the same topic. In use, the user needs to create a concrete implementation of the abstract factory, and then use the abstract factory as an interface to create concrete objects for the method. It belongs to the creation pattern.
Abstract factories seem to be an extension of the factory approach, with the concept of a product family, a collection of products. The factory method above has one product (Button), while the abstract factory example below has two products (Button and Border).
Advantages of abstract factories: Concrete products are separated from customer code. Easy to change the product line. The creation of a unified family of products from a family.
Disadvantages of abstract factories: Extending new products in a product family is difficult and requires modifying the interface of the abstract factory.
Wikipedia’s example of an elephant factory
public interface Button {}
public interface Border {}
public class WinButton implements Button {}
public class WinBorder implements Border {}
public class MacButton implements Button {}
public class MacBorder implements Border {}
public interface AbstractFactory {
Button createButton(a);
Border createBorder(a);
}
public class WinFactory {
@Override
public static Button createButton(a) {
return new WinButton();
}
@Override
public static Border createBorder(a) {
return newWinBorder(); }}public class MacFactory {
@Override
public static Button createButton(a) {
return new MacButton();
}
@Override
public static Border createBorder(a) {
return newMacBorder(); }}Copy the code
Builder Pattern
The Builder pattern can be used when building a complex object. It is essentially passing in a parameter and then returning the object itself for the next property or parameter to build on. It can build objects on demand and has strong scalability. It belongs to the creation pattern.
In the JDK, the Append () method of the StringBuilder class is a good example of a build pattern.
// java.lang.StringBuilder
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
Copy the code
Prototype Pattern
Its feature is that it returns a new instance by copying an existing one, rather than creating a new one. The copied instance is what we call a prototype, which is customizable. It belongs to the creation pattern.
The prototype pattern is often used to create complex or time-consuming instances, because in this case it is more efficient to run a program by copying an existing instance, which is essentially the same type of data with different names.
In the JDK, the clone() method in the Object class is a typical prototype pattern.
// Can be overridden directly in the implementation classclone() method protected native Objectclone() throws CloneNotSupportedException;
Copy the code
Singleton Pattern
Whenever an object is retrieved, only the same instance is returned. Usually when we read the configuration file, the contents of the file are unchanged, so we can use the singleton pattern to achieve this. It belongs to the creation pattern.
The singleton pattern is simple and requires only three steps to complete. The following is the implementation of the singleton pattern by the Runtime class in the JDK.
Public Class Runtime {// 1.new private static Runtime currentRuntime = new Runtime(); // 2. Return the current application Runtime instance public static RuntimegetRuntime() {
returncurrentRuntime; } // 3. The private constructor does not allow the Runtime instance private to be constructed in other classesRuntime() {}}Copy the code
Adapter Pattern
The adapter pattern, also known as wrapper, is a structural pattern.
Its purpose is to pass two incompatible interfaces through an adapter or wrap them as compatible interfaces so that they can work together. For example, the data cable of our mobile phone is just like an adapter. One end is USB interface, and the other end is Type-C or Lightning interface. Originally, they cannot work together, but we can make them work together with the data cable (adapter).
It has the following advantages: 1. It allows any two unrelated interfaces to work together. 2. Improved interface reuse. 3. Increased interface transparency. 4. Good flexibility.
Its disadvantages: 1. Too much use of adapters will make the system very messy, not easy to grasp as a whole. 2. Since JAVA can inherit only one class, it can adapt at most one adapter class, and the target class must be abstract.
In the JDK, the asList(T… A) Methods are examples of adapter patterns that convert an array to a collection.
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
Copy the code
In the JDK, the list() method of the Collections utility class turns enumerations into Collections.
public static <T> ArrayList<T> list(Enumeration<T> e) {
ArrayList<T> l = new ArrayList<>();
while (e.hasMoreElements())
l.add(e.nextElement());
return l;
}
Copy the code
Of course, the above two examples are very simple and don’t seem to use any design patterns, just like the transformations we normally use. This is just an introduction to the idea that, in practice, an intermediate Adapter class is used for compatibility or packaging.
Bridge Pattern
Separate the abstraction from its implementation so that both can vary independently. It belongs to the structural pattern.
Object-oriented programming features become useful when a class changes frequently, because the program code can be easily changed with minimal prior knowledge of the program. The bridge pattern is useful when the class and it changes frequently. Classes themselves can be thought of as abstractions, and classes can act as implementations. The bridge pattern can also be thought of as a two-layer abstraction.
It decouples abstraction and realization by providing a bridge between them.
Its advantages: 1. Abstraction and implementation are separated. 2. Excellent ability to expand. 3. Implementation details are transparent to the caller.
Its disadvantages: the introduction of bridge mode will increase the difficulty of understanding and designing the system. Since the aggregation association relationship is established at the abstraction layer, it needs to design and program for abstraction to deepen the programming difficulty.
This is an example of producing different vehicles and requiring different production processes. First, define an abstract interface for a Vehicle, which contains a corresponding process set for producing the Vehicle and an abstract method for producing it. Then comes the realization of abstract interface by Bike and Car. Then define a process required to produce the vehicle (WorkShop), which has two implementations: ProduceWorkShop and TestWorkShop. Finally, the code for the main method is how this bridge pattern is used.
The advantage of this design pattern is that it is easy to add a Bus that simply inherits the Vehicle class. It is also very convenient to add a production process PaintWorkShop, which only needs to inherit the WorkShop class and has high scalability.
Public abstract class Vehicle {// protected List<WorkShop> Workshops = new ArrayList<WorkShop>(); publicVehicle() {
super();
}
public boolean joinWorkshop(WorkShop workshop) {
returnworkshops.add(workshop); } public abstract void manufacture(); } public class Bike extends Vehicle {@override public voidmanufacture() {
System.out.println("Manufactoring Bike..."); workshops.stream().forEach(workshop -> workshop.work(this)); }} public class Car extends Vehicle {@override public voidmanufacture() {
System.out.println("Manufactoring Car"); workshops.stream().forEach(workshop -> workshop.work(this)); Public abstract class WorkShop {public abstract void work(Vehicle Vehicle); } public class ProduceWorkShop extends WorkShop {publicProduceWorkShop() {
super();
}
@Override
public void work(Vehicle vehicle) {
System.out.print("Producing... "); Public class TestWorkShop extends WorkShop {publicTestWorkShop() {
super();
}
@Override
public void work(Vehicle vehicle) {
System.out.print("Testing... "); Public class Main {public static void Main (String[] args) {public static void Main (String[] args) { bike.joinWorkshop(new ProduceWorkShop()); bike.manufacture(); Vehicle car = new car (); car.joinWorkshop(new ProduceWorkShop()); car.joinWorkshop(new TestWorkShop()); car.manufacture(); }}Copy the code
Finally, it’s very simple to look at examples of design patterns, but sometimes you don’t use them when writing code, which is a shift in thinking about writing code. That is, before writing code, we need to think about which design patterns are appropriate for the business scenario, and remember that the ultimate goal of using design patterns is readable, cohesive, low-coupling, and extensible code. This is a change in thinking, thinking more in doing, practice makes perfect.
PS: Clear mountains and clear waters begin with dust, knowledge is more valuable than diligence. Wechat official account: “Clean the dust chat”. Welcome to chat and talk about code.