Several simple design patterns

1. Template mode

The simple understanding of template design pattern is templating, standardization, and also a form of calculation and solution coupling. For example, two classes implement an interface, such as student class and teacher class, which inherit the classes of Tsinghua University. How can they distinguish between teachers and students? Very simple, let both students and teachers have a type method to indicate their identity, When introducing to the outside world, say "I'm a teacher of Tsinghua University or I'm a student of Tsinghua University", in which "I'm from Tsinghua University" is a template, and the rear teachers and students are their own unique methods. Then the template can be put into the Tsinghua university class and become public, and the specific teachers and students need to be implemented. Then I need three classes, Tsinghua University, teachers and students.

abstract class qinghua{
    public void show(){
        System.out.println("I'm from Tsinghua University"+getType());//Template
    }
    abstract String getType();//Need to achieve
}
class teacher extends qinghua{
    @Override
    String getType() {
        return "teacher";
    }
}
class student extends qinghua{
    @Override
    String getType() {
        return "student";
    }
}
public class Demo {
    public static void main(String[] args) throws IOException {
       student s =new student();
       s.show();
    }
}

Print "I'm a student of Tsinghua University". Therefore, the template pattern is actually extracted from the parent class. The structure is very simple

legend:

2. Decoration mode (packaging mode)

Decoration mode is to wrap a class in order to rewrite a method or some methods of an existing class. The most example is to rewrite the closing method of jdbc link pool link connection. If a handwritten link pool has been developed before, the connection pool obtains the connection. After the link is used up, it should be put back into the pool rather than closed. If the original drivermanager The connection obtained by getconnection () is a subclass implemented by MySQL driver. Because connection itself is an interface and only provides standard methods, the specific implementation is a subclass. Then the close method is to close it directly, but we don't want to close it and want to put the link back to the pool. Therefore, we need to rewrite the close method of connection, but we can't affect other methods, We have no source code for the specific subclass of connection. What should we do? This uses the decoration mode.

//Steps:
/*1,Write a class that implements the same interface as the wrapped class. (with the same behavior)
2,Defines a variable of the wrapped class type.
3,Define the construction method, inject the object of the wrapped class and assign a value to the variable of the wrapped class.
4,For methods that do not need to be rewritten, call the original method.
5,For methods that need to be rewritten, write your own code.*/
public class MyConnection implements Connection {
    private Connection con;
    private LinkedList<Connection> pool;

    public MyConnection(Connection con, LinkedList<Connection> pool) {
        this.con = con;
        this.pool = pool;
    }

    @Override
    public void close() throws SQLException {
        pool.addLast(con);
    }
    @Override
    public Statement createStatement() throws SQLException {
        return con.createStatement();
    }

Let's look at MyConnection. This class implements the connection interface, implements all its methods, and rewrites the close method. We pass in the specific connection subclass through the construction method and use it to implement other methods. Finally, the package of close is completed. This is somewhat similar to the proxy model.

legend:

3. Adapter mode

The adapter serves as a bridge between the caller and the owner. What the caller wants to call is not consistent with what the owner can give, so it needs to be coordinated by an intermediary. The question here is, why not write another one? Do you have to call it? Therefore, the adapter is not set at the time of design, but when a function needs to be put into a new environment during development, It is only used when it is incompatible with the new environment and needs to be modified. Because we don't want to change the original class, but we need it to adapt to the new environment. For example, if the memory card on the mobile phone needs to be able to read on the computer, it needs a card reader. For another example, the power supply is 220v, but my computer uses 110v. What should I do? You can't change the wire to 110v, so there is a converter to convert 220v to 110v, which is the adapter.

First, let's take a look at the example in java. The adapter of the listener. In the GUI of java, there is a window class Frame, which can create a window, but the close button of the window cannot be used. You must add a close event to the button. In java, it is set through the close of the event listener. The code is as follows:

public static void main(String[] args) throws IOException {
       Frame f =new Frame();
       f.setBounds(100,200,300,300);
       f.addWindowListener();//A listener needs to be added to the parameter here, but the listener is an interface and cannot be instantiated, so we need to customize the implementation and rewrite the relevant methods
    }

So let me implement windosListener

public class MyListener implements WindowListener {
    @Override
    public void windowOpened(WindowEvent e) {
        
    }

    @Override
    public void windowClosing(WindowEvent e) {

    }

    @Override
    public void windowClosed(WindowEvent e) {

    }

    @Override
    public void windowIconified(WindowEvent e) {

    }

    @Override
    public void windowDeiconified(WindowEvent e) {

    }

    @Override
    public void windowActivated(WindowEvent e) {

    }

    @Override
    public void windowDeactivated(WindowEvent e) {

    }
}

It is found that there are too many methods that need to be rewritten after implementation, but we don't need these. We only need one of the windowClosing methods, that is, closing. Of course, we can do nothing else, but so many methods are redundant, which is not what we want. What should we do?

We can write another abstract class, implement windosListener and rewrite those methods, and then inherit this abstract class with our custom listener. Because the abstract class has implemented all the methods, our listener only needs to choose what we want, (in the above decoration mode, Myconnection also needs to rewrite many methods, and you can also add an adapter, but the adapter also needs to rewrite many methods, so it is unnecessary)

public class MyListener extends MyAdater {

    @Override
    public void windowClosing(WindowEvent e) {
       
    }
}

Here MyAdater is a middleman, an adapter. In java, a written adapter WindowAdapter is also provided, so you can use it directly without implementing it yourself

public static void main(String[] args) throws IOException {
       Frame f =new Frame();
       f.setBounds(100,200,300,300);

       f.addWindowListener(new WindowAdapter() {
           @Override
           public void windowClosing(WindowEvent e) {
               super.windowClosing(e);
           }
       });
    }

We click to see the source code and find that, like our MyAdater, we have implemented the WindowListener interface and, of course, other interfaces to make it more applicable.

Let's take another example. Our current mobile phones have only one typec interface, and there is no headphone interface. What can we do if we want to plug in headphones? We can use an adapter line.

Here we need to clarify several objects

1. Target interface: or standard interface, the interface we expect. For example, headphone connector

2. Adapted object: an existing class, but it is incompatible with what we want. For example, mobile phone interface

3. Adapter: it is a kind of adapter that can combine the above two together

public class TypeC {
    //Existing interfaces, classes to be adapted
   public void typcIO(){
       System.out.println("typec Interface");
   }
}
public interface Omtp {
    public void omtpIo();//The interface that the headset can use, omtp interface
}
public class TypeCAndOmtpAdapter extends TypeC implements Omtp{
    @Override
    public void omtpIo() {//Rewrite the target interface and connect the interface of the existing class
        System.out.println("Can link typec of omtp Interface");
        this.typcIO();
    }
}

When using, you can directly use the adapter class.

 public static void main(String[] args) throws IOException {
       Omtp erji =new TypeCAndOmtpAdapter();
       erji.omtpIo();
    }

At this point, we can use the adapter to call our desired method, but we can use our existing logic. It's equivalent to connecting round hole headphones with typec interface.

But if our typc is also different, one is Android and the other is apple. We require that the connection cable can be connected. What should we do? Let's change the adapter.

public class TypeCAndOmtpAdapter implements Omtp{

    private TypeC typeC;

    public TypeCAndOmtpAdapter(TypeC typeC) {
        this.typeC = typeC;
    }

    @Override
    public void omtpIo() {//Rewrite the target interface and connect the interface of the existing class
        System.out.println("Can link typec of omtp Interface");
        typeC.typcIO();
    }
}

Define typc as an interface and create two other implementation classes

public class TypeCApple implements TypeC{

    @Override
    public void typcIO() {
        System.out.println("Apple's interface");
    }
}
public class TypeCAndriod implements TypeC{

    @Override
    public void typcIO() {
        System.out.println("Android interface");
    }
}

We upgraded the adapter and passed in our existing classes using construction methods, which can be used again.

The above three examples are actually three types of adapter mode. The first is interface adapter, the second is class adapter, the third is object adapter, and the latter two are actually one, but there are some differences in implementation methods.

 

Difference between adapter mode and decoration mode:

From the object adapter of the adapter, it is very similar to the implementation method of decoration mode. But their goals are different

Decoration mode: the target and custom decoration classes implement the same interface and have the same behavior as the target class. They just need to rewrite some methods of the target class, turn them into their own methods and complete the packaging.

Adapter mode: the target is not consistent with the existing class or interface, nor is it the same type. The adapter only converts in the middle and takes both, so that the two cooperate to complete the conversion.

4. Factory mode

Factory mode, as the name suggests, is the place where things are generated, and in Java, the place where classes are produced. This is a creation mode. Often, when some of your objects need to be classified but interrelated, you often need to make different choices when creating. In order to reduce coupling, we should extract the creation behavior as much as possible and concentrate it in the factory, So there is the factory model.

1. Simple factory

Simple factory is very simple, which is the most commonly used extraction. There are also cats and dogs. For example, instantiate a cat or dog, usually use new, and collect the work of new into the factory after extraction

public class AnimalFactory {
    public static Animal getAnimal(String type){
        Animal a =null;
        if("Cat".equals(type)){
            a= new Cat();
        }else if("Dog".equals(type)){
            a= new Dog();
        }
        return a;
    }
}

use:

 public static void main(String[] args) throws Exception {
        Animal cat = AnimalFactory.getAnimal("Cat");
        cat.eat();
    }

In this way, new Cat and new Dog are extracted, which is a little clear and simple, and the amount of code is small. The disadvantage is that we must have the permission to modify the source code of factory class. This is generally the case when developing factories developed by ourselves.

2. Factory method mode

What if we can't modify the source code of the factory? At this time, the design of the factory needs to be extended outward. Using the polymorphic characteristics of java, we can expand the factory method, create cat factory and dog factory, and rewrite the method of the total factory.

public interface AnimalFactory {
   public Animal getAnimal();
}
public class CatFactory implements AnimalFactory {
    @Override
    public Animal getAnimal() {
        return new Cat();
    }
}
public class DogFactory implements AnimalFactory {
    @Override
    public Animal getAnimal() {
        return new Dog();
    }
}

use:

 public static void main(String[] args) throws Exception {
        AnimalFactory factory =new DogFactory();
        Animal dog = factory.getAnimal();
        dog.eat();
    }

This method can not change AnimalFactory, but each time you add a new class, you need to add a new concrete factory at the same time. For example, if you want to add a pig, you need to write a pig factory.

The advantage is that it is easy to expand and does not need to change the source code.

The disadvantage is that the amount of code is large and troublesome.

legend;

3. Abstract factory mode

The above two factories are to create an animal, such as dog, cat, pig and so on. So what if I want to create white cats and white dogs, black cats and black dogs, or create chickens and ducks in addition to cats and dogs. Cats and dogs belong to one category, chickens and ducks belong to another category, but they are all animals. According to the previous method, there should be two different factories, pet factory and poultry factory, but they are all animals, so they should all belong to animal factory. Therefore, the abstract factory mode is the "factory of factory" mode.

Join me. Now there are two simple factory patterns, PetFactory (PET factory) and PoultryFactory (poultry factory). How can they be extracted into an AnimalFactory? We can use the form of factory method pattern to let the two factories inherit an abstract factory AnimalFactory and rewrite the methods in it.

PetFactory and related classes:

public class PetFactory  extends AnimalFactory{
    public Pet getPet(String type){
        Pet p =null;
        if("cat".equals(type)){
            p =new Cat();
        }else {
            p =new Dog();
        }
        return p;
    }

    @Override
    public Poultry getPoultry(String type) {
        return null;
    }
}
public interface Pet {
    public  void eat();
}
public class Cat implements Pet {
    @Override
    public void eat() {
        System.out.println("How cats eat");
    }
}
public class Dog implements Pet{
    @Override
    public void eat() {
        System.out.println("How dogs eat");
    }
}

PoultryFactory factory and related classes

public class PoultryFactory  extends AnimalFactory{

    public Poultry getPoultry(String type){
        Poultry poultry =null;
        if("cat".equals(type)){
            poultry =new Chicken();
        }else {
            poultry =new Duck();
        }
        return poultry;
    }

    @Override
    public Pet getPet(String type) {
        return null;
    }

}
public interface Poultry {
    void eat();
}
public class Chicken implements Poultry {
    @Override
    public void eat() {
        System.out.println("Chickens eat rice");
    }
}
public class Duck implements Poultry{
    @Override
    public void eat() {
        System.out.println("Ducks can swim");
    }
}

Both factories inherit AnimalFactory

public abstract class AnimalFactory {
    public abstract Pet getPet(String type);
    public abstract Poultry getPoultry(String type);
}

Here, we have completed the abstract factory. AnimalFactory can be created in the way of factory method pattern, but according to the Convention, another factory provider will be created to wrap the operation of creating a specific factory in the form of simple factory pattern. Provide factory type of factory

public class FactoryProducer {
    public AnimalFactory getAnimalFactory(String type){
        if("pet".equals(type)){
            return new PetFactory();
        }else if("poultry".equals(type)){
            return new PoultryFactory();
        }
        return null;
    }
}

use;

public static void main(String[] args) throws Exception {
        AnimalFactory f = FactoryProducer.getAnimalFactory("poultry");
        Poultry duck = f.getPoultry("duck");
        duck.eat();
    }

This factory pattern is more complex, but it is also based on the combination of simple factory and factory method pattern for further extraction.

legend:

5. Singleton mode

Singleton mode is used in many places. The so-called singleton is an instance. To achieve this goal, there is a simple way in our usual code, such as defining a static static member variable in a class, so that we can achieve the purpose of sharing, that is, all objects share this variable.

The same principle applies to singleton mode. However, in order to make a class have only one object and be shared by all objects, in addition to being static, there is another point that it cannot be instantiated casually and can only be instantiated once. Here is a key point: privatize the construction method. Only after the construction method is privatized, others can't new. The rest is the creation method. There are several creation methods.

1. Hungry Han style

public class SingleObj {
    private SingleObj() { }//Privatization of construction method is very critical
    
    private static  SingleObj singleObj =new SingleObj();//Define a static object and instantiate it directly
    
    public  static SingleObj getInStance(){//Provide our instantiated objects through public methods.
        return singleObj;
    }
}

This method is the most commonly used and relatively simple. In java, the Runtime class uses this method. The disadvantage of this method is that it takes up a certain amount of memory, especially if your object is large, because you have to instantiate an object to take up space. However, in terms of current computer configuration, if there are few places where singleton mode is required, it is harmless, so it is used a lot.

2. Lazy style

public class SingleObj {
    private SingleObj() { }//Privatization of construction method is very critical

    private static  SingleObj singleObj ;//Define a static variable and do not give an instance call object. The default is null

    public  static synchronized SingleObj getInStance(){//Provide our instantiated objects through public methods.
        if(singleObj==null){
            return singleObj;
        }
       return singleObj;
    }
}

Lazy "lazy" is derived from lazy of "lazy loading", that is, lazy loading. Lazy loading is instantiated when it is used, which can solve the problem of space occupied by instantiation at the beginning. However, when the getInStance method is called by multiple threads, singleObj is null at the same time, so the method needs to add the synchronized keyword, but in this way, In the case of multithreading, the performance is affected. This is also its disadvantage.

These two methods are the two most commonly used basic forms. In addition to these two, there are many other implementation methods, such as double retrieval, static internal class (Registration), enumeration and so on. You can refer to other blogs.

Blog of a great God

 

 

 

 

Keywords: Java Design Pattern

Added by satan165 on Fri, 28 Jan 2022 13:27:37 +0200