A familiar design pattern

Write before

To judge whether a programmer is good or not is to show me the code.Good code readability, high cohesion, low coupling, scalability.To write good code and be a good programmer, you need to look at the open source framework written by Taurus, absorb the essence of it, learn more about design patterns, and there are no other shortcuts.

There are three main types of design modes: creation mode, structure mode and behavior mode.

Factory method pattern

Define an interface that creates objects so that the class that implements the interface decides which class to instantiate.The factory method defers instantiation of a class to a subclass, which is a creation mode.

Factory objects usually contain one or more methods for creating various types of objects that this factory can create.These methods may receive parameters that specify how the object was created and, ultimately, return the created object.

A factory is usually an object used to create other objects.Factories are abstractions of construction methods used to implement different allocation schemes.

Examples of Wikipedia factory methods

// Defines how Button is created
public interface Button{}
// Implement WinButton 
public class WinButton implements Button{}
// Implement MacButton 
public class MacButton implements Button{}

// Create a factory class for Button
public interface ButtonFactory {
    Button createButton();
}
// Really create the implementation class for WinButton and implement ButtonFactory
public class WinButtonFactory implements ButtonFactory {
    @Override
    public static Button createButton(){
        return new WinButton();
    }
}
// Really create MacButton's implementation class, implement ButtonFactory
public class MacButtonFactory implements ButtonFactory {
    @Override
    public static Button createButton(){
        return new MacButton();
    }
}

Abstract factory pattern

Encapsulate a group of separate factories with the same theme.In use, the user needs to create a concrete implementation of the abstract factory, then use the abstract factory as the interface to create the concrete object of this method.It belongs to the creation mode.

An abstract factory is like an extension of the factory method, with the concept of a family of products, a bunch of products.The above factory method is for one product (Button), while the following abstract factory example has two products (Button and Border).

Advantages of abstract factories:
Specific products are separated from the customer code.
Easy to change product range.
Create a family of products together.

Disadvantages of abstract factories:
Extending a new product within a product family is difficult and requires modifying the interface of an abstract factory.

Example of Wikipedia's Abstract 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();
    Border createBorder();
}

public class WinFactory {
    @Override
    public static Button createButton() {
    return new WinButton();
    }
    @Override
    public static Border createBorder() {
    return new WinBorder();
    }
}

public class MacFactory {
    @Override
    public static Button createButton() {
    return new MacButton();
    }
    @Override
    public static Border createBorder() {
    return new MacBorder();
    }
}

Builder pattern

When building a complex object, you can use the Builder pattern.It essentially passes in a parameter and returns the object itself for the next property or parameter to build.Objects can be built on demand and scalable.It belongs to the creation mode.

The append() method of the StringBuilder class is a good example of a construction pattern in JDK.

    // java.lang.StringBuilder
    @Override
    public StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }

Prototype pattern

It is characterized by returning a new instance by copying an existing one instead of creating a new one.The replicated instance is what we call the prototype, which is customizable.It belongs to the creation mode.

Prototype patterns are often used to create complex or time-consuming instances in which copying an existing instance makes the program run more efficiently, essentially identical data with different names.

In JDK, the clone() method in the Object class is a typical prototype pattern.

    //clone() methods can be overridden directly in specific implementation classes
    protected native Object clone() throws CloneNotSupportedException;

Singleton pattern

Whenever you get an object, 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 mode to achieve this.It belongs to the creation mode.

The singleton pattern is simple and takes only three steps to complete. Below is the singleton pattern implemented by the Runtime class in the JDK.

public class Runtime {
    // 1.new A private static Runtime instance
    private static Runtime currentRuntime = new Runtime();

    // 2. Return the Runtime instance of the current application
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    // 3. Privatization construction methods, do not allow Runtime instances to be constructed in other classes
    private Runtime() {}
}

Adapter pattern

Adapter mode, also known as wrapper, is a structural mode.

Its purpose is to wrap two otherwise incompatible interfaces through one adapter or into compatible interfaces, and then they can work together.For example, our mobile phone cable is like an adapter, with a USB interface on one end and a Type-C or Lightning interface on the other. They don't work together, but we can use this cable (adapter) to make them work together.

Its advantages:
1. Any two interfaces that are not associated can work together.
2. Improved interface reuse.
3. Increase the transparency of the interface.
4. Flexibility.

Its drawbacks:
1. Using adapters too much will make the system very messy and not easy to master as a whole.
2. Since JAVA can inherit only one class, it can only be adapted to at most one adapter class, and the target class must be abstract.

In JDK, the asList(T... a) method in the Arrays class is an example of adapter mode, converting an array into a collection.

    public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

In the JDK, the list() method of the Collections tool 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;
    }

Of course, these two examples are very simple, as if you can't see any design patterns used, just like the conversions we usually use.This is just an introduction to the idea that, in fact, an intermediate Adapter class is used for compatibility or packaging.

Bridge pattern

Separate the abstraction from its implementation so that the two can change independently.It is a structural pattern.

Object-oriented programming can be very useful when a class changes frequently because the code of a program can be easily changed with minimal prior knowledge of the program.Bridge mode is useful when the class and it change frequently.Classes themselves can be considered abstract, and classes can be implemented.Bridge patterns can also be thought of as two levels of abstraction.

It decouples abstraction from realisation by providing a bridge between them.

Its advantages:
1. Separate abstraction from implementation.
2. Excellent scalability.
3. Implementation details are transparent to the caller.

Its drawbacks:
The introduction of bridging mode makes it more difficult to understand and design the system. Since aggregation relationship is built on the abstraction layer, it is necessary to design and program against the abstraction to make the programming more difficult.

This is an example of producing different vehicles and requiring different production processes.First, define an abstract interface for a vehicle (Vehicle) with a corresponding set of processes for producing the vehicle and an abstract method for producing the vehicle.Then there is the implementation of the abstract interface for the Bike and the Car.Then define a process (WorkShop) needed to produce a vehicle with two implementations of ProduceWorkShop and TestWorkShop.Finally, the code for the main method is how to use this bridge pattern.

The benefit of this design pattern is that it's easy to add a Bus, simply inheriting the Vehicle class.It is also very convenient to add a PaintWorkShop, simply inheriting the WorkShop class, which is very scalable.

//Abstract interface of vehicle
public abstract class Vehicle {
    //
    protected List<WorkShop> workshops = new ArrayList<WorkShop>();
    public Vehicle() {
        super();
    }
    public boolean joinWorkshop(WorkShop workshop) {
        return workshops.add(workshop);
    }
    public abstract void manufacture();
}

//Realization of bicycles
public class Bike extends Vehicle {
    @Override
    public void manufacture() {
        System.out.println("Manufactoring Bike...");
        workshops.stream().forEach(workshop -> workshop.work(this));
    }
}
//Realization of automobiles
public class Car extends Vehicle {
    @Override
    public void manufacture() {
        System.out.println("Manufactoring Car");
        workshops.stream().forEach(workshop -> workshop.work(this));
    }
}
//Abstract interface for production vehicle
public abstract class WorkShop {
    public abstract void work(Vehicle vehicle);
}
//Realization of Manufacturing Vehicles
public class ProduceWorkShop extends WorkShop {
    public ProduceWorkShop() {
        super();
    }
    @Override
    public void work(Vehicle vehicle) {
        System.out.print("Producing... ");
    }
}
//Implementation of Test Vehicle
public class TestWorkShop extends WorkShop {
    public TestWorkShop() {
        super();
    }
    @Override
    public void work(Vehicle vehicle) {
        System.out.print("Testing... ");
    }
}
//Use
public class Main {
    public static void main(String[] args) {
        //Make a bicycle
        Vehicle bike = new Bike();
        bike.joinWorkshop(new ProduceWorkShop());
        bike.manufacture();
        //Make a car
        Vehicle car = new Car();
        car.joinWorkshop(new ProduceWorkShop());
        car.joinWorkshop(new TestWorkShop());
        car.manufacture();
    }
}

Finally, it is very simple to look at design patterns alone, but sometimes you don't use them when writing code, which is a change in the way you think about writing code.That is, before writing code, we need to think about business scenarios, which design patterns are appropriate to use, keeping in mind that the ultimate goal of using design patterns is code readability, high cohesion, low coupling, and scalability.This is a change in thinking, think more and practice makes life.

PS:
Clear mountains and green waters start with dust, and knowledge is more important than diligence.
WeChat Public Number: "Dust and Chat".
Welcome to talk and code.

Keywords: Java JDK Programming Mobile

Added by serg91 on Mon, 10 Jun 2019 19:20:30 +0300