[Learn about common design patterns]

Article directory


img

Learn about common design patterns (factory, packaging, relationship)

Introduction

**Design Pattern** is a solution to general problems in the software development process. It is a summary of the experience of countless object-oriented software developers and is very important for software design and development. However, due to the wide variety of design patterns and the theoretical content, it is difficult to understand related concepts due to lack of development experience. At the same time, there are many similar types of design patterns, which makes it even more difficult to learn design patterns.

This article aims to consolidate the understanding of design patterns and understand the characteristics of different design patterns .

The data sources are as follows:

Design pattern analysis series

Analysis of Design Patterns - Factory (Simple Factory, Static Factory, Factory Method, Abstract Factory)

For the design pattern using the factory, after understanding the usage scenarios and concepts, just remember the following sentences to ensure mastery.

  1. So factories are used to encapsulate the creation of objects
  2. All factory patterns promote loose coupling by reducing dependencies between applications and concrete classes
  3. Factories help us program against abstractions rather than against concrete classes

Analysis of design patterns - packaging (decoration mode, adapter mode, appearance mode, proxy mode)

Remember the purpose of the four design patterns in the packaging article:

  • Decoration pattern: wrap an object to add new behaviors and responsibilities
  • Adapter pattern: wrap an object to change its interface
  • Facade pattern: Wrap a group of objects to simplify their interface
  • Proxy Pattern: Wrap an object to control access to it

Analysis of Design Patterns - Relationships (Bridge Pattern, Combination Pattern, Flyweight Pattern, Intermediary Pattern)

Bridge mode, composition mode and mediator mode all change the relationship between classes or objects , thereby achieving their respective goals of reducing the number of objects or reducing code complexity.

  • The bridge mode uses the principle of composition/aggregation reuse , changing inheritance into aggregation, **separating the abstract part from its implementation part, **reducing the number of subclasses and reducing the coupling of the code

  • The combination mode uses a tree structure to represent the "part-whole" hierarchical structure to manage objects, allowing users to use a unified interface to use all objects in the combination structure, simplifying the code and improving scalability.

  • The mediator pattern uses mediators to control and coordinate interactions between a group of objects, converting many-to-many relationships into one-to-many , reducing the coupling between objects and improving code reusability and scalability.

  • The flyweight mode is mainly to solve the problem of memory resource waste. It uses sharing technology to reduce object instances by extracting similar parts between objects.

The key to distinguishing these design patterns still lies in grasping the differences in purpose (intention).

Factory

factory

Find the parts that change and separate them from the parts that don’t change

When using interfaces during software development, a large number of concrete classes often need to be instantiated in the code, and most of these implementations are determined by some conditions . Regarding the issue of how to instantiate objects, it is often considered to use a separate class to handle the process of creating instances. This class is a factory .

what is a factory

Programming for interfaces, not programming for implementation

Definition: Used to encapsulate the code that creates objects. The class responsible for handling the details of creating objects is called a factory.

Advantages: Concentrate the code for creating objects into one object or method to avoid duplication in the code and facilitate future maintenance.

Simple Factory "Pattern"

The simple factory "pattern" is the most basic application for factories, but it cannot actually be counted as a "factory pattern". It is not a design pattern, but a programming habit.

Simple Factory Code Example: Simple Calculator

Implement a simple calculator through code, with operation functions of addition, subtraction, multiplication and division

public class OperationFactory {
    
    
    public static Operation createOperation(char operator) {
    
    
	Operation operation = null;

	switch (operator) {
    
     //可以看到简单工厂将运算类的实例放在工厂类中实现
//通过分支选择具体的实现类
	case '+':
	    operation = new OperationAdd();
	    break;
	case '-':
	    operation = new OperationSub();
	    break;
	case '*':
	    operation = new OperationMul();
	    break;
	case '/':
	    operation = new OperationDiv();
	    break;
	default:
	    throw new RuntimeException("unsupported operation");
	}

	return operation;
    }
}
public class OperationAdd extends Operation {
    
    

    @Override
    public double result() {
    
     //具体的操作子类只需要实现具体运算
		return numberA + numberB;
    }

}
public class Calculator {
    
    
    public static void main(String[] args) {
    
    
        Operation operation;
        char operator;

        operator = '+';
    //使用过程中,在客户端提供具体实例的参数,传入工厂类实现
        operation = OperationFactory.createOperation(operator);
        operation.numberA = 1.2;
        operation.numberB = 2.3;
    //具体的运算过程,客户端不可见
        System.out.println(operation.result());
    }
}

Open-closed principle: open to extension, closed to modification

advantage:

  1. Dynamically instantiate relevant classes according to the client's selection conditions, eliminating the client's dependence on specific classes.
  2. Adding a new class only requires adding a new branch in the factory class

shortcoming:

  1. It does not comply with the "open-closed principle". Every extension in a simple factory requires modification of the factory class.

static factory pattern

Defining the function of creating objects in the factory class as static is the static factory pattern, which is also not a design pattern.

Features:

  1. No need to instantiate the object using the method that creates the object
  2. You cannot change the behavior of a creation method through inheritance

Since there is not much difference between a static factory and a simple factory, we will not go into details here. More information about the static factory mode is described in detail in this article "https://zhuanlan.zhihu.com/p/157099580". If you are interested, you can learn more.

Factory Method Pattern

What is the factory method pattern?

The factory method pattern defines an interface for creating objects, letting subclasses decide which class to instantiate. The factory method defers the instantiation of a class to its subclasses.

Factory Method Pattern Code Example: Simple Calculator

Implement a simple calculator through code, with operation functions of addition, subtraction, multiplication and division

public interface IFactory {
    
     //所有工厂的抽象,工厂接口
    public Operation createOperation(); //创建实例类的方法,延迟到工厂子类中实现
}
//工厂的具体实现类
class AddFactory implements IFactory {
    
    
    @Override
    public Operation createOperation() {
    
     //实现对操作类的实现 
		return new OperationAdd();
    }
}

class SubFactory implements IFactory {
    
    
    @Override
    public Operation createOperation() {
    
    
		return new OperationSub();
    }
}

class MulFactory implements IFactory {
    
    
    @Override
    public Operation createOperation() {
    
    
		return new OperationMul();
    }
}

class DivFactory implements IFactory {
    
    
    @Override
    public Operation createOperation() {
    
    
		return new OperationDiv();
    }
}

public class FactoryClient {
    
    
    public static void main(String[] args) {
    
    
    //由客户端选择实例化的工厂类(除法)
        IFactory operFactory = new DivFactory();
        Operation operation = operFactory.createOperation(); //进行运算操作

        operation.numberA = 3.4;
        operation.numberB = 4.5;
        System.out.println(operation.result());
    }
}

advantage

  1. By letting the factory subclass decide what object should be created, the purpose of encapsulating the object creation process is achieved.
  2. Programming for interfaces, not implementation programming, the code is more flexible and has better scalability
  3. Comply with the open-closed principle : the client decides which factory to instantiate to create a class, and does not need to modify the factory class when extending
  4. Decoupling: Move the specific implementation of creating objects to specific factory subclasses. The factory interface class does not need to know which class the object is actually created. This decouples the superclass code and subclass object creation in the client. code.

Similarities and differences with simple factories:

same:

  1. Centrally encapsulates the creation of objects, reducing the client's dependence on specific implementations and coupling with product objects.

different:

  1. The simple factory handles everything about instantiation in the factory class (there is no abstract factory class), but the factory method pattern creates a framework that lets the factory subclass decide how to implement it.
  2. It overcomes the shortcomings of simple factories that violate the "open-closed principle" and maintains the advantages of encapsulating the object creation process, further abstracting and promoting simple factories.

Abstract Factory Pattern

Dependency inversion principle: rely on abstractions, not concrete classes

  • Guidelines to avoid violating the Dependency Inversion Principle in OO design:

    • Variables cannot hold references to concrete classes
    • Don't let a class derive from a concrete class
    • Do not override methods already implemented in base classes

What is the abstract factory pattern?

The Abstract Factory pattern provides an interface for creating a series of related or interdependent objects without specifying their specific classes.

Each method in the abstract factory creates a concrete class, which is actually implemented using the factory method.

Abstract Factory Pattern Code Example: Calculator Production

Assume that there is an assembly line responsible for producing calculator buttons and assembling and inspecting calculators. The color series of calculator buttons include black series, white series, etc. that need to be implemented.

//按钮工厂
public interface ButtonFactory{
    
    
//生产四种不同的运算按钮
    public AddButton createAddButton();
    public SubButton createSubButton();
    public DivButton createDivButton();
    public MulButton createMulButton();
}
/**
* 需要提前说明的是WhiteAddButton是AddButton的具体实现类,
* 不同的运算按钮并不是同一类,代码省略了Button相关类的声明
*/
//白色系列按钮工厂
public class WhiteButtonFactory implements ButtonFactory{
    
    
    @Override
    public AddButton createAddButton() {
    
    
    	return new WhiteAddButton();
    }
    @Override
    public SubButton createAddButton() {
    
    
    	return new WhiteSubButton();
    }
    @Override
    public DivButton createAddButton() {
    
    
    	return new WhiteDivButton();
    }
    @Override
    public MulButton createAddButton() {
    
    
    	return new WhiteMulButton();
    }
}

//黑色系列按钮工厂
public class BlackButtonFactory implements ButtonFactory{
    
    
    @Override
    public AddButton createAddButton() {
    
    
    	return new BlackAddButton();
    }
    @Override
    public SubButton createAddButton() {
    
    
    	return new BlackSubButton();
    }
    @Override
    public DivButton createAddButton() {
    
    
	    return new BlackDivButton();
    }
    @Override
    public MulButton createAddButton() {
    
    
    	return new BlackMulButton();
    }
}
//计算器类
public class Calculator() {
    
    
    AddButton addButton;
    DivButton divButton;
    SubButton subButton;
    MulButton mulButton;
    public Calculator(ButtonFactory buttonFactory) {
    
    
		this.buttonFactory = buttonFactory;
    }
    public void prepare() {
    
    
    	addButton = buttonFactory.createAddButton();
    	subButton = buttonFactory.createSubButton();
    	divButton = buttonFactory.createDivButton();
		mulButton = buttonFactory.createMulButton();
    }
    //组装、测试方法
    public void assembly();
    public void test();
}
public abstract class AssemblyLine {
    
    
    //生产方法
    public Calculator produce(){
    
    
    	Calculator calculator;
    	calculator = createCalculator();
    	calculator.prepare();
    	calculator.assembly();
    	calculator.test();
    	return calculator;
    }
    protected abstract Calculator createCalculator();
}
//白色按钮流水线
public class WhiteAssemblyLine extends AssemblyLine {
    
    
    @Override
    protected  Calculator createCalculator(){
    
    
    	Calculator calculator = null;
    	ButtonFactory buttonFactory = new WhiteButtonFactory();
    	calculator = new Calculator(buttonFactory);
        return calculator;
    }
}
//黑色按钮流水线
public class BlackAssemblyLine extends AssemblyLine {
    
    
    @Override
    protected  Calculator createCalculator(String type){
    
    
    	Calculator calculator = null;
    	ButtonFactory buttonFactory = new BlackButtonFactory();
    	calculator = new Calculator(buttonFactory);
    	return calculator;
    }
}
public class CalculatorClient() {
    
    
    public static void main(String[] args) {
    
    
    	//声明流水线
    	AssemblyLine whiteLine = new WhiteAssemblyLine();
    	AssemblyLine blackLine = new BlackAssemblyLine();
    	//决定生成的计算器实例,可以看到系列按钮的创建过程与客户端分离
    	Calculator calculator1 = whiteLine.produce();
    	Calculator calculator2 = blackcLine.produce();
    }
}

It should be noted that since the usage scenario of the abstract factory class is for "series", the scenario of the code example here has changed and involves many classes. Please be sure to understand it. The main function of the abstract factory class is to encapsulate the creation of different series of buttons. Therefore, when changing the button series of the calculator, we only need to replace the pipeline class (the pipeline class here is actually just one of the factory classes). Encapsulation can be simply understood as replacing the factory class). You can imagine that if you are using a simple factory or factory method pattern, how should you write the code to achieve the functions of the above code example? When changing the button series, what needs to be changed?

advantage

  1. Easy to exchange product series. Changing specific factory classes only needs to be done during initialization. It is very convenient to use different product configurations.
  2. Allow customers to use abstract interfaces to create a specific set of products, and the creation process is separated from the client, reducing coupling.

shortcoming

  1. When increasing demand, you need to change the interface and make changes to each specific factory class, which is more complicated.

Similarities and differences with the factory method pattern:

same:

  1. Centrally encapsulates the creation of objects, reducing the client's dependence on specific implementations and coupling with product objects.

different:

  1. Create object
  • Factory method pattern: inheritance

    • To create an object through a factory subclass, the client only needs to know the specific abstract type. The factory subclass determines the specific type and is only responsible for decoupling the client from the specific type.
  • Abstract factory pattern: composition of objects

    • Provides an abstract class used to create a product family. Subclasses of this type define the method by which products are created. When used, they instantiate a specific factory class and pass the factory class into the code written for the abstract type to remove customers from all aspects. Decoupled from the actual specific product used
  1. The abstract factory collects a group of related product classes, and the factory method only needs to create one product. Each method in the abstract factory creates a concrete class, which is actually implemented using factory methods.

expand

  • Improve abstract factory with simple factory:

    • Reduce specific factory classes to achieve decoupling purposes
  • Reflection + abstract factory:

    • Eliminate the use of switch using dependency injection

Summarize

For the design pattern using the factory, after understanding the usage scenarios and concepts, just remember the following sentences to ensure mastery.

  1. So factories are used to encapsulate the creation of objects
  2. All factory patterns promote loose coupling by reducing dependencies between applications and concrete classes
  3. Factories help us program against abstractions rather than against concrete classes

Packaging

Introduction

When you are writing code and need to extend the functionality of a class, or the interface of the current class cannot meet your needs, what will you choose to do? Rewrite the subclass and add functionality through inheritance? Modify the interface of the original class to make it conform to the existing environment? But you will find that these changes are not perfect, and they do not comply with the object-oriented "open-close principle".

Open-closed principle: open for extension, closed for modification

There's a better answer in software design patterns - packaging .

The four design patterns introduced below all revolve around "packaging", so first let's briefly understand these design patterns:

  • Decorator Pattern : wraps another object and provides additional behavior
  • Adapter Pattern : wraps another object and provides a different interface
  • Facade Pattern : Wrap many objects to simplify their interfaces
  • Proxy Pattern : wrap another object and control access to it

Decorator Pattern

When you want to extend the functionality of a class, the most direct way is to write a subclass and then add new functions to the subclass. But this kind of change often leads to many problems, such as when you want to remove a method in the parent class. This is not a flexible design and does not comply with the "open-closed principle". Decoration mode provides a new idea besides inheritance.

What is decoration mode?

Definition: Dynamically attach responsibilities to objects and add additional responsibilities to objects. For extended functionality, decorators provide a more flexible method than inheritance.

  • The decorator can add its own behavior before or after the behavior of the entrusted decorator to achieve a specific purpose.
  • In the process of using the decorator pattern, you can use multiple decoration classes to wrap objects (there is no limit to the number), and the client can selectively use decoration functions to wrap objects at runtime .

Decorator & decorated object

The decorator and the decorated object need to have the same parent class. The decorator pattern uses inheritance to achieve type matching.

  • The implementation of each decoration object is separated from how to use the object. Each decoration object only cares about its own function and does not need to care about how to be added to the object chain.

Usage: Pass the object that needs to be instantiated into the decoration class for packaging

The http://Java.IO library is written in the decorator pattern

Decoration mode code example:

//Component定义了一个对象接口,通过装饰类可以给这些对象动态地添加职责
public abstract class Component {
    
    
    public abstract void operation();
}
/** Decorator,装饰抽象类,继承了Component
* 从外类来扩展Component类的功能,但对于Component来说,
* 是无需知道Decorator的存在的
*/
public abstract class Decorator extends Component {
    
    
	protected Component component;
//获取被装饰的对象
    public Component getComponent() {
    
    
	return component;
    }
//设置被装饰的对象
    public void setComponent(Component component) {
    
    
		this.component = component;
    }
    @Override
    public void operation() {
    
    
        if (component != null) {
    
    
        	component.operation();
        }
    }
}
//具体装饰类,可以为类加入新的行为
class ConcreteDecoratorA extends Decorator {
    
    
    private String addedState;

    @Override
    public void operation() {
    
    
        // 首先运行原Component的operation(),再执行本类的功能,如addedState,相当于对原Component进行了装饰
        super.operation();
        addedState = "A中的new state ";
        System.out.println(addedState + "具体装饰对象A的操作");
    }
}

class ConcreteDecoratorB extends Decorator {
    
    
    @Override
    public void operation() {
    
    
        super.operation();
        addedBehavior();
        System.out.println("具体装饰对象B的操作");
    }
    public void addedBehavior() {
    
    
		System.out.print("B中的新增行为 ");
    }
}

class ConcreteDecoratorC extends Decorator {
    
    
    @Override
    public void operation() {
    
    
        super.operation();
        System.out.println("C没有特殊行为 " + "具体装饰对象C的操作");
    }

}
//ConcreteComponent是定义一个具体的对象,也可以给这个对象添加一些职责
public class ConcreteComponent extends Component {
    
    
    @Override
    public void operation() {
    
    
		System.out.println("具体对象的操作");
    }

}
//装饰模式客户端调用代码
public class DecoratorClient {
    
    
    public static void main(String[] args) {
    
    
        ConcreteComponent concreteComponent = new ConcreteComponent();
        //声明装饰类A、B、C
        ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA();
        ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB();
        ConcreteDecoratorC concreteDecoratorC = new ConcreteDecoratorC();
        //装饰的过程就像是层层包装,不断地装饰类包装对象,达到添加功能的目的
        concreteDecoratorA.setComponent(concreteComponent); //装饰类A包装对象
        concreteDecoratorB.setComponent(concreteDecoratorA); //装饰类B包装装饰类A(对象已经被包装在装饰类A中)
        concreteDecoratorC.setComponent(concreteDecoratorB); //装饰类C包装装饰类B
        concreteDecoratorC.operation();
    }
}

In DecoratorClient, after being packaged by the decoration class, the final object relationship is as follows. It is packaged in the decoration class by the decorator concreteComponent, and has additional methods and behaviors of each decoration class.

The packaging relationship of each object:

img

The difference between decorator pattern and inheritance:

  1. Inherited design subclasses are statically determined at compile time. Objects are extended through combination and can be dynamically extended at runtime.
  2. The decorator pattern can dynamically add new behaviors to objects at runtime through composition and delegation.

Advantages and Disadvantages of Decoration Mode

advantage:

  • Move the decorative functions in the class out of the class and simplify the original class
  • Effectively separate the core responsibilities and decorative functions of the class
  • Remove duplicate decoration logic in related classes

shortcoming:

  • The decorator pattern often results in a large number of small classes in the design. Too much data may complicate the program and cause problems for programmers who use the API.
  • When a decorator instantiates a component, it not only instantiates the component but also wraps the component into the decorator, which increases code complexity.
  • To overcome this shortcoming, the instantiation part of the code is encapsulated through factory mode and generator mode.

What is adapter pattern?

Definition: The adapter pattern converts the interface of a class into another interface expected by the client . The adapter allows classes with incompatible interfaces to work together.

Adapters wrap adapters with modified interfaces using object composition.

The adapter converts the adapter's interface into the adapter's interface.

Applicable scene

  1. When you need to use an existing class but its interface does not meet the requirements
  2. When two classes do the same or similar things but have different interfaces, you can use the adapter pattern to unify the interfaces .

Adapter pattern code example:

//客户所期待的接口
public abstract class Target {
    
    
    public void request() {
    
    
	System.out.println("普通请求!");
    }
}

//适配器类,通过在内部包装一个Adaptee对象,把原接口转换成目标接口
public class Adapter extends Target {
    
    
	//内部的Adaptee对象
    private Adaptee adaptee = new Adaptee();
    @Override
    public void request() {
    
      //适配器类提供的客户端需要的接口
	adaptee.specificRequest();
    }
}

//需要适配的类
public class Adaptee {
    
    
    public void specificRequest() {
    
    
	System.out.println("特殊的请求!");
    }

}

//适配器客户端
public class AdapterClient {
    
    
    public static void main(String[] args) {
    
    
	Target target;
	target = new Adapter(); //直接调用适配器类,内部包装了Adaptee
	target.request();
    }
}

Advantages and Disadvantages of Adapter Pattern

advantage:

  • There is no need to make a lot of changes to the code. By adding an adapter class, the changes are encapsulated in the class to meet the new requirements.
  • Decouple clients from implemented interfaces/adapters

shortcoming:

  • Resulting in more "wrapper" classes to handle communication with other components, leading to increased complexity and development time, and reduced runtime performance
  • Too many classes coupled together will increase maintenance costs and reduce code understandability.

The adapter mode should only be considered when encountering situations where the original design and code cannot be changed. During the design and development process, interface mismatch problems should be prevented in advance. When small interface inconsistencies occur, they should be reconstructed in time. Avoid expanding the problem

Type of adapter pattern

Class adapter: inherit the adapter class and target class
  • Class adapters match one interface to another through multiple inheritance

  • advantage:

    • Since the adapter class is a subclass of the adapter class, some adapter methods can be replaced in the adapter class to make the adapter more flexible.
  • shortcoming:

    • For languages ​​such as Java and C# that do not support multiple inheritance, only one adapter class can be adapted at a time.
    • The target abstract class can only be an interface, not a class. Its use has certain limitations. An adapter class and its subclass cannot be adapted to the target interface at the same time.
Object adapter: uses combination to transmit the request to the adaptee
  • Can adapt to a class and any of its subclasses

  • advantage:

    • Adapt multiple different adapters to the same target, that is, the same adapter can adapt both the adapter class and its subclasses to the target interface.
  • shortcoming:

    • Adapt multiple different adapters to the same target, that is, the same adapter can adapt both the adapter class and its subclasses to the target interface.

Differences from decorator pattern:

  • Decorator pattern: used when new functionality needs to be added

    • Add new functionality to a class without changing existing code
  • Adapter mode: used when you need to convert the interface

    • Allow users to use new libraries and sub-collections without changing the original code

Least Knowledge Principle, also known as Law of Demeter

  1. Each unit has limited knowledge about other units and only knows about units that are closely related to the current unit, i.e. only talks to your close friends

Policy: Only call methods belonging to the following scopes

  1. the object itself
  2. The object passed in as a parameter of the method
  3. Any object created or instantiated by this method
  4. any component of the object

Facade Pattern

When there are many complex interfaces that need to be used, encapsulating the complex logic through a class and providing a simple unified interface can greatly improve the readability of the code and reduce the complexity of the program.

The appearance pattern is such a class, but it does not "encapsulate" the subsystem. Use the facade pattern when you need to simplify and unify a large interface or a complex group of interfaces.

What is appearance mode?

Definition: The facade pattern provides a unified interface for accessing a set of interfaces in a subsystem. This pattern defines a high-level interface that makes the subsystem easier to use.

  • Multiple appearances can be created

Applicable scene

By using the facade pattern, a "facade" is established between the layers of the data access layer, business logic layer and presentation layer to reduce coupling.

Appearance mode code example:

//“系统”接口,只是标记接口,暂无任何意义
public interface SystemInterface {
    
    

}
//子系统类
class SubSystemOne implements SystemInterface {
    
    
    public void methodOne() {
    
    
		System.out.println("子系统方法一");
    }
}

class SubSystemTwo implements SystemInterface {
    
    
    public void methodTwo() {
    
    
		System.out.println("子系统方法二");
    }
}

class SubSystemThree implements SystemInterface {
    
    
    public void methodThree() {
    
    
		System.out.println("子系统方法三");
    }
}

class SubSystemFour implements SystemInterface {
    
    
    public void methodFour() {
    
    
		System.out.println("子系统方法四");
    }
}
//外观类,包括了所有的子系统的方法或属性,进行组合,以备外界调用
public class Facade {
    
    
//子系统
    SubSystemOne subSystemOne;
    SubSystemTwo subSystemTwo;
    SubSystemThree subSystemThree;
    SubSystemFour subSystemFour;

    public Facade() {
    
    
        subSystemOne = new SubSystemOne();
        subSystemTwo = new SubSystemTwo();
        subSystemThree = new SubSystemThree();
        subSystemFour = new SubSystemFour();
    }
//外观类提供的统一接口
    public void methodA() {
    
    
        System.out.println("方法组A:");
        subSystemOne.methodOne();
        subSystemTwo.methodTwo();
        subSystemFour.methodFour();
    }

    public void methodB() {
    
    
        System.out.println("方法组B:");
        subSystemThree.methodThree();
        subSystemFour.methodFour();
    }
}
//外观类客户端
public class FacadeClient {
    
    
    public static void main(String[] args) {
    
    
        // 由于Facade的作用,客户端可以根本不知道四个子系统的存在
        Facade facade = new Facade();
        facade.methodA();
        facade.methodB();
    }
}

When maintaining a large legacy system, it may be very difficult to maintain and extend the system. At this time, a Facade class can be developed for the new system to provide a clearer and simpler interface for the poorly designed or highly complex legacy code, allowing the new system to The system interacts with the Facade object, and the Facade does all the complex work with the legacy code.

Appearance mode pros and cons:

advantage:

  • Decouple the client from the subsystem. The client code is written towards the interface. If you want to modify the components of the subsystem, you only need to change the appearance class.
  • By implementing a facade class that provides a more reasonable interface, we can hide the logic behind complex subsystems and provide a convenient interface.
  • There is no "encapsulation" of subsystem classes, only simplified interfaces are provided, and subsystem classes can still be called. While providing a simplified interface, the complete functions of the system are exposed for use by those who need it.
  • Follow the " principle of least knowledge "

shortcoming:

  • Without introducing an abstract appearance class, adding a new subsystem may require modifying the source code of the appearance class or the client, which violates the " open-close principle "

Differences from decoration mode and adapter mode:

  • Decorator: does not change the interface, but adds responsibilities
  • Adapter: converts one interface into another interface, changing the interface to meet customer expectations
  • Appearance: Make the interface simpler and provide a simplified interface for the subsystem

Proxy Pattern

Agency literally means acting for someone else. In computers, proxies are often used to control and manage access, such as proxy servers, which can serve as transfer stations to obtain network information on behalf of network users.

What is proxy mode

Definition: The proxy pattern provides a stand-in or placeholder for another object in order to control client access to the object.

  • Use the proxy mode to create a representative and let the representative object control access to an object. The proxy object can be a remote object, an object that is expensive to create, or an object that requires security control.

Agent control access method:

  1. Remote proxy: controls access to remote objects and provides local representation of an object in different geographical spaces
  2. Virtual proxy: Controls access to resources that are expensive to create, and uses proxies to replace real objects that take a long time to instantiate (picture boxes when loading web pages)
  3. Protection Agent: Control access to resources based on permissions

Agent mode code example:

The proxy here is just a simple example. Actual proxies are often used in the scenarios mentioned above (remote, virtual, protection) to control and manage access.

//接口类,定义真实实体类与代理类共用的接口
public interface Subject {
    
    
    public void request();
}
//实体类
public class RealSubject implements Subject {
    
    
    @Override
    public void request() {
    
    
		System.out.println("真实对象的请求");
    }
}
//代理类
public class Proxy implements Subject {
    
    

    // 保存一个引用,使得代理可以访问真实实体
    Subject subject;
    public Proxy() {
    
    
		subject = new RealSubject();
    }
    @Override
    public void request() {
    
    
		subject.request();
    }

}
//代理客户端
public class ProxyClient {
    
    
	public static void main(String[] args) {
    
    
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

Advantages and disadvantages of proxy mode:

advantage:

  • The proxy pattern introduces a degree of indirection when accessing objects, and this indirection can be attached for a variety of purposes
  • The proxy mode acts as an intermediary between the client and the target object and can protect the target object
  • Separates the client from the target object, reducing coupling to a certain extent.

shortcoming:

  • Increases the complexity of the system, the client can only see the proxy class
  • There will be a lot of duplicate code.

Differences from decoration mode and adapter mode

  • Decorators add behavior to objects, while proxies control access to objects
  • Proxies and adapters both stand in front of other objects and are responsible for forwarding requests to them
  • Adapters change the interface that an object adapts to, while proxies implement the same interface.

Agent pattern variant types (simple understanding)

  • Firewall Proxy: Controls access to network resources and protects themes from "bad clients"
  • Smart Reference Proxy: When the topic is referenced, additional actions are performed and the proxy handles other things. For example, count the number of times an object is referenced
  • Caching Proxy: Provides temporary storage for expensive calculation results: it also allows multiple clients to share results to reduce computing or network delays
  • Synchronization Proxy provides secure access to topics in multi-threaded situations
  • Complexity Hiding Proxy: used to hide the complexity of a complex collection of classes and perform access control, sometimes also called Facade Proxy
  • Copy-On-Write Proxy: Used to control the replication of objects by delaying the replication of objects until the client really needs it. This is a variant of the virtual proxy.

Summarize

For decoration mode, adapter mode, appearance mode and proxy mode, they are all similar to each other. For example, all four of them wrap objects, adapter mode and appearance mode both provide interfaces, and both proxy mode and decoration mode are possible. New features will be introduced…

But in fact, the key point in distinguishing these design patterns is not how to package the class, the number of package classes, and whether to add new functions. The key point is that the purpose (intention) of different design patterns is different.

The purpose of the four design patterns

  • Decoration pattern: wrap an object to add new behaviors and responsibilities

    • Don’t change the interface, but add responsibilities
  • Adapter pattern: wrap an object to change its interface

    • Convert one interface to another and change the interface to meet customer expectations
  • Facade pattern: Wrap a group of objects to simplify their interface

    • Make the interface simpler and provide a simplified interface to the subsystem
  • Proxy Pattern: Wrap an object to control access to it

    • Control and manage access to objects

After grasping the above points, you will remember the essential differences between the four design patterns. I believe you will have a deeper understanding of the applicable scenarios for each design pattern.

Relationships

Introduction

In the previous part, in view of the problems that inheritance statically determines classes at compile time and has poor scalability, the decoration mode provides a more flexible method than inheritance to add functional functions to classes. This part further discusses the potential problems of inheritance , focusing on classes. and the relationship between objects . The bridge mode , combination mode and flyweight mode all belong to the "structural design pattern", while the intermediary mode belongs to the "behavioral design pattern" . They are placed in this part for better comparison of several modes, despite the naming of the relationship chapter. It may not be accurate enough, but it can still summarize the common points of these design patterns.

inherit

As one of the three basic characteristics of object-oriented, inheritance is the most common class relationship and its functions are as follows:

  • By inheriting the characteristics and behaviors of the parent class, the subclass makes the objects (instances) of the subclass have the attributes and methods of the parent class.
  • Or the subclass inherits methods from the parent class so that the subclass has the same behavior as the parent class.

Advantages: Inheritance can be extended by using subclasses to override parent class methods, which improves code reusability and achieves a certain degree of scalability.

Disadvantages: Excessive use of inheritance will cause the class structure to be too complex, too many relationships between objects, the code will be difficult to maintain, and the scalability will be poor .

Potential issues with inheritance relationships

  1. The inheritance relationship of an object is statically defined at compile time, and the implementation inherited from the parent class cannot be changed at runtime .
  2. The implementation of the subclass has a close dependence on the parent class. Any changes in the implementation of the parent class will lead to changes in the subclass.
  3. When reusing a subclass, the inherited implementation may not be suitable for solving new problems. In this case, the parent class must be rewritten or replaced. This dependency limits flexibility and reusability.

The relationship between subclasses and parent classes is a highly coupled relationship.

Bridge Pattern

A parent class can implement multiple changes through subclasses. When class changes are more complex, using inheritance alone will cause a large number of classes to be added and cannot satisfy the open-closed principle. Inheritance is a strongly coupled structure . Our design goal is to find a weakly coupled and loosely coupled structure to represent the relationship between abstract classes and implementations. This design pattern is the bridge pattern .

The bridge mode decouples the changes in different directions of the class and uses object combination to change the inheritance relationship between the two roles into a combination relationship , so that the two roles can cope with independent changes.

What is bridge mode?

As the name suggests, "bridge" plays a connecting role. In the bridge mode, the abstraction and implementation of two independent structures are connected through the "bridge" . These two parts can be independently expanded and changed in an inheritance manner without affecting each other.

Definition: The Bridge pattern separates the abstract part from its implementation so that they can both vary independently.

  • The separation of abstraction and implementation does not mean the separation of abstract classes and derived classes.
  • Implementation refers to the abstract class and its derived classes used to implement its own objects. Implementation can be classified from multiple angles (directions)

The core intention of the bridge pattern is to separate the implementations of each category, allow them to change independently , and reduce the coupling between them. Changes in each implementation will not affect other implementations, thereby achieving the purpose of coping with changes.

How does bridge mode respond to changes?

  • Find the change package
  • Prioritize the use of object aggregation rather than inheritance, that is, the composition/aggregation reuse principle

Composition/Aggregation Reuse Principle (CARP)

Try to use composition/aggregation and try not to use class inheritance.

Composition : Represents a strong "ownership" relationship, embodying a strict relationship between part and whole. The life cycle of the part and the whole is the same.

Aggregation : Represents a weak "ownership" relationship, reflecting that object A contains object B, but object B is not part of object A.

Composition and clustering are both special kinds of association.

benefit:

  • Composition/aggregation of limited-use objects will help keep each class encapsulated and focused on a single task.
  • In this way, classes and class inheritance hierarchies can be kept small and will not grow too much and become uncontrollable.

Bridge mode diagram

Suppose there is a book production system now. The books produced are of two types: Book1 and Book2, with two different colors: yellow and green. There is an existing abstract class Book. If you only use inheritance, you will need four subclasses as shown below. meet needs.

If you want to add a book type or a color type, you need to declare a new extension to the subclass. The number of required subclasses will explode and the code complexity will continue to increase.

Just use inheritance:

img

Using the bridge mode, you can separate the book type (abstraction) and color type (implementation), and change the inheritance to a combination. Extensions and changes will be carried out from two independent perspectives, and there is no need to add subclasses. Implement expansion.

Bridge mode:

img

The color-related code will be extracted into the color class, and the book and color parts will be connected by adding member variables pointing to the color object in the book class. After using the bridge mode, adding new colors will not affect the existing shape classes. On the contrary, adding new shapes will not affect the existing color classes. New functions or methods only need to extend the class, no need Modify existing classes to comply with the "open-closed" principle.

Bridge mode code example:

public abstract class Abstraction {
    
    
    protected Implementor implementor;// 桥接模式的关键,使得Abstraction聚合Implementor
    private String name;

    public Abstraction(String name) {
    
    
		this.setName(name);
    }
	//抽象类所聚合的实现类
    public void setImplementor(Implementor implementor) {
    
    
		this.implementor = implementor;
    }
	//相关方法
    public void operation() {
    
    
		System.out.print("Abstraction-" + this.getName() + ": ");
		implementor.operation();
    }
    public String getName() {
    
    
		return name;
    }
    public void setName(String name) {
    
    
		this.name = name;
    }
}
//抽象实现类A
class AbstractionA extends Abstraction {
    
    
    public AbstractionA(String name) {
    
    
		super(name);
    }
    @Override
    public void operation() {
    
    
		super.operation();
    }
}
//抽象实现类B
class AbstractionB extends Abstraction 
    public AbstractionB(String name) {
    
    
		super(name);
    }
    @Override
    public void operation() {
    
    
		super.operation();
    }
}
public abstract class Implementor {
    
    
    public abstract void operation();
}

class ConcreteImplemtorA extends Implementor {
    
    
    @Override
    public void operation() {
    
    
		System.out.println("ConcreteImplemtorA的方法执行");
    }
}

class ConcreteImplemtorB extends Implementor {
    
    
    @Override
    public void operation() {
    
    
		System.out.println("ConcreteImplemtorB的方法执行");
    }
}
//客户端
public class BridgeClient {
    
    
    public static void main(String[] args) {
    
    
    	Abstraction a = new AbstractionA("A");//抽象类A实例
    	a.setImplementor(new ConcreteImplemtorA());//设置包含的具体实现类
    	a.operation();
    	a.setImplementor(new ConcreteImplemtorB());
    	a.operation();
    		
    	Abstraction b = new AbstractionB("B");//抽象类B实例
    	b.setImplementor(new ConcreteImplemtorA());
    	b.operation();
    	b.setImplementor(new ConcreteImplemtorB());
    	b.operation();
    }
}

Advantages and Disadvantages of Bridge Mode

advantage:

  • It achieves the separation of abstraction and implementation and reduces the coupling between them.
  • Multi-angle classification implementation objects can reduce the complexity of the project and the relationship between classes, and different angles will not affect each other.
  • New functions or methods only need to extend the class and do not need to modify the existing class. It conforms to the "open-closed" principle and improves the scalability of the class.

shortcoming:

  • The introduction of the bridge mode will increase the complexity of the system and increase the difficulty of code understanding and design.
  • The scope of use of the bridge mode has certain limitations. It requires the identification of two or more independently changing dimensions in the system before it can be used.

Composite Pattern

What is combination mode?

Definition: Combining objects into a tree structure to represent a "part-whole" hierarchy. The combination mode allows users to process single objects and combined objects in a consistent way.

  • Manage objects using a tree structure
  • Ability to move between individual objects and grouped objects
  • Basic objects can be combined into more complex combined objects, and combined objects can be combined. Everything in the code that uses basic objects can use combined objects.

The Composite pattern allows us to create a structure of objects in the form of a tree, which contains combinations and individual objects. Composite objects can be used anywhere basic objects are used, and clients can use composite structures consistently with individual objects.

Combination, component, leaf node, branch node

A composition is composed of components . Any object in the composition structure is called a component. There are two types of components:

  • leaf node element
  • Branch node element (combination class)

Combined tree structure:

The root is the top-level combination, the branch nodes are below, and the leaf nodes are at the end.

  • In the composite pattern, all objects belong to the same class, and the whole and parts can be treated consistently.
  • Using composition structures, we can apply the same operations to compositions and individual objects. In most cases, we can ignore the difference between compositions of objects and individual objects.

Classification of combination modes

Transparent mode

Declare all methods used to manage sub-objects in the component interface, so that the sub-objects (leaf nodes and branch nodes) are indistinguishable from the outside world and have completely consistent behavioral interfaces .

Transparency: The combination model exchanges the single responsibility design principle for "transparency". The interface of the component also contains some operations for managing child nodes and leaf nodes. Both child nodes and leaf nodes are transparent to users.

Problems:

  • There is a problem that the leaf node class does not have relevant methods and implements meaningless methods, which violates the single responsibility principle, that is, the principle of least knowledge.
  • Users may perform some inappropriate or meaningless operations and lose some security
safe way

Implement all methods used to manage sub-objects in the composite object. Do not declare relevant methods in the component class. The client needs to make corresponding judgments when calling.

Security: Differentiate responsibilities and place them on different interfaces. The code needs to judge and process different types of nodes through conditional statements, etc., which loses transparency, but this design is safer.

The security method conforms to the single responsibility principle and interface isolation principle of the design pattern.

Problems:

  • The client needs to distinguish branch nodes and child nodes in order to handle operations at different levels. It cannot rely on abstraction, which violates the dependency inversion principle of the design pattern.

Summary: When implementing the composition pattern, transparency and security need to be balanced as needed.

Combination mode code example:

/**
 * Component为组合中的对象提供统一的接口,在适当情况下,实现所有类共有接口的默认行为。
 * 使用组合结构的所有类都继承该类
 */
public abstract class Component {
    
    
    protected String name;

    public Component(String name) {
    
    
		this.name = name;
    }

    public abstract void add(Component component);

    public abstract void remove(Component component);

    public abstract void display(int depth);

}
// 组合类(枝节点),用来存储子部件,实现了组件接口中的相关操作
public class Composite extends Component {
    
    
    private List<Component> children = new ArrayList<Component>();

    public Composite(String name) {
    
    
		super(name);
    }

    @Override
    public void add(Component component) {
    
    
		children.add(component);
    }

    @Override
    public void remove(Component component) {
    
    
		children.remove(component);
    }

    @Override
    public void display(int depth) {
    
    
        // 显示其枝节点名称,并对其下级进行遍历
        System.out.println(StringUtil.repeatableString("-", depth) + this.name);		
        for (Component component : children) {
    
    
            component.display(depth + 2);
        }
    }
}
//叶节点类,组合模式中的最小粒度,没有子节点
public class Leaf extends Component {
    
    

    public Leaf(String name) {
    
    
		super(name);
    }

    @Override
    public void add(Component component) {
    
    
		System.out.println("cannot add to a leaf");
    }

    @Override
    public void remove(Component component) {
    
    
		System.out.println("cannot remove from a leaf");
    }

    @Override
    public void display(int depth) {
    
    
		// 通过“-”的数目显示级别
		System.out.println(StringUtil.repeatableString("-", depth) + this.name);
    }

}
public class CompositeClient {
    
    
	// 展现了组合结构的构成过程
    public static void main(String[] args) {
    
    
        // 生成树根,根上长出两叶Leaf A和Leaf B
        Composite root = new Composite("root");
        root.add(new Leaf("Leaf A"));
        root.add(new Leaf("Leaf B"));

        // 根上长出分支Composite X,分支上也有两叶Leaf X-A和Leaf X-B
        Composite compositeX = new Composite("Composite X");
        compositeX.add(new Leaf("Leaf X-A"));
        compositeX.add(new Leaf("Leaf X-B"));
        root.add(compositeX);

        // 在Composite X上再长出分支Composite X-Y,分支上也有两叶Leaf X-Y-A和Leaf X-Y-B
        Composite compositeXY = new Composite("Composite X-Y");
        compositeXY.add(new Leaf("Leaf X-Y-A"));
        compositeXY.add(new Leaf("Leaf X-Y-B"));
        compositeX.add(compositeXY);

        // 显示大树的样子
        root.display(1);
    }
}

When to use combo mode

  1. When requirements reflect part and whole hierarchical structures, users are expected to ignore the differences between composite objects and single objects, and when using all objects in the composite structure uniformly , they should consider the composite pattern.
  2. When there are multiple object collections in the program , and there is a "whole/part" relationship between the objects, and you want to process these objects in a consistent way , you need the combination pattern

Advantages and Disadvantages of Combination Model

advantage:

  • Users do not need to care that the object is a composite object. The composite pattern allows users to use composite structures and single objects consistently.
  • The addition of leaf nodes in the combination mode is very convenient, conforms to the open-close principle, and is easy to maintain.

shortcoming:

  • Leaf nodes and branch nodes are implementation classes, not interfaces, which violates the dependency inversion principle.
  • The introduction of composite classes increases the complexity of the design, and the client needs to spend time clarifying the hierarchical relationship between classes.

Flyweight Pattern

In programming, sometimes it is necessary to use a large number of objects, but using many objects will cause excessive memory overhead. If a large number of objects are repeated , it will cause a waste of resources. The flyweight mode is proposed to address this problem and can avoid the overhead of a large number of similar classes .

  • If these instances are basically the same except for a few parameters, you can greatly reduce the number of classes that need to be instantiated.
  • If you can move those parameters outside the class instance and pass them in when the method is called, you can greatly reduce the number of individual instances through sharing.

What is flyweight mode?

Use sharing technology to efficiently support large numbers of fine-grained objects

  • Essence: Cache shared objects to reduce memory consumption.
  • Flyweight pattern is an improvement of factory method pattern

Status classification of flyweight objects

internal state

A shared part that is inside the Flyweight object and does not change as the environment changes.

external state

Changes as the environment changes and cannot be shared.

The main roles of the flyweight model:

Flyweight Factory

Used to create and manage Flyweight objects. It is mainly used to ensure reasonable sharing of Flyweight. When the user requests a Flyweight, a created instance is provided for the object. If it does not exist, a new Flyweight instance is created for the object.

Abstract flyweight class (Flyweight)

The superclass or interface of all specific flyweight classes. Through this interface, Flyweight can accept and act on external states.

Concrete Flyweight

Inherit the Flyweight superclass or implement the Flyweight interface and add storage space for internal state

Unshared Concrete Flyweight

Refers to Flyweight subclasses that do not need to be shared

Flyweight mode code example:

//享元超类
public abstract class FlyWeight {
    
    
	//接受并作用于外部状态的方法
    public abstract void operation(int extrinsicState);
}

class ConcreteFlyWeight extends FlyWeight {
    
    
    @Override
    public void operation(int extrinsicState) {
    
    
		System.out.println("具体FlyWeight:" + extrinsicState);
    }
}

class UnsharedConcreteFlyWeight extends FlyWeight {
    
    

    @Override
    public void operation(int extrinsicState) {
    
    
		System.out.println("不共享的具体FlyWeight:" + extrinsicState);
    }
}
public class FlyWeightFactory {
    
    
    private HashMap<String, FlyWeight> flyWeights = new HashMap<String, FlyWeight>();

    public FlyWeight getFlyWeight(String key) {
    
    
        if (!flyWeights.containsKey(key)) {
    
    
            flyWeights.put(key, new ConcreteFlyWeight());
        }
        return flyWeights.get(key);
    }
}
public class FlyWeightClient {
    
    
    public static void main(String[] args) {
    
    
        int extrinsicState = 22;
        FlyWeightFactory f = new FlyWeightFactory();

        FlyWeight fx = f.getFlyWeight("X");
        fx.operation(--extrinsicState);

        FlyWeight fy = f.getFlyWeight("Y");
        fy.operation(--extrinsicState);

        FlyWeight fz = f.getFlyWeight("Z");
        fz.operation(--extrinsicState);

        FlyWeight uf = new UnsharedConcreteFlyWeight();
        uf.operation(--extrinsicState);
    }
}

When to use flyweight mode

  • Flyweight mode should be considered when an application uses a large number of objects that generate huge storage overhead.
  • Most of the state of an object can use external state. When deleting the external state of an object can replace many groups of objects with relatively few shared objects, you can consider using the Flyweight pattern.

Advantages and Disadvantages of Flyweight Model

advantage:

  • The use of shared objects in the Flyweight mode can greatly reduce the total number of object instances and save storage overhead . The amount of savings increases with the increase in shared state.
  • Avoid the use of a large number of fine-grained objects without affecting the program, and use sharing technology to effectively support a large number of fine-grained objects.

shortcoming:

  • It is necessary to maintain a list that records all flyweights in the system, and the list also consumes resources.
  • The flyweight mode will increase the complexity of the system . Object sharing requires externalizing some states, which will increase the logical complexity of the program. It is worthwhile to use the flyweight mode only when there are enough object instances in the program to be shared.

Application of flyweight model

  1. The string string in NET uses the flyweight mode. The same instance is used for the same string object, and the memory sharing of the string is realized by reference pointing.
  2. In chess games, the color of the chess pieces is the internal state, and the position is the external state. Taking Go as an example, using flyweight mode, only two instances of black and white chess pieces can be declared, which greatly reduces the memory overhead.

Mediator Pattern

When a large number of objects are used in programming, the relationships between objects and the system code logic are complex, and the scalability is poor, which is not conducive to coping with changes. The mediator pattern avoids this problem by encapsulating the collective behavior of objects.

What is the intermediary pattern?

Definition: Use an intermediary object to encapsulate a series of object interactions. Mediators eliminate the need for objects to explicitly reference each other, making them loosely coupled and allowing them to independently change their interactions.

  • The mediator pattern is easy to apply in the system and easy to misuse. Before considering using the intermediary model, you must consider the rationality of the system design.
intermediary

The mediator pattern encapsulates collective behavior into an independent mediator object

  • The mediator is responsible for controlling and coordinating the interaction between a group of objects. When acting as a mediator, the objects no longer explicitly reference each other. The objects only know the mediator, which reduces the number of interconnections. The degree of coupling between classes is reduced, which is conducive to reuse.
Colleagues

To realize the specific classes of business, the behaviors between different classes are controlled and coordinated through intermediaries.

Mediator pattern diagram

Multi-object relationship diagram:

img

Mediator pattern object relationship diagram:

img

The mediator model allows objects to only communicate with the mediator, and the relationship between objects is transformed into a star structure , which can effectively reduce the coupling of the system.

Mediator pattern and bridge pattern:

  • The bridge mode converts the many-to-many relationship within a class into a many-to-one relationship between multiple classes outside the class.
  • The mediation model converts the many-to-many relationship of multiple classes into a one-to-many relationship. This makes the code's responsibilities more single and more conducive to reuse, expansion, and testing.

Mediator pattern code example:

//抽象中介类
public abstract class Mediator {
    
    
    public abstract void send(String message, Colleague colleague);
}
//具体中介类
class ConcreteMediator extends Mediator {
    
    
    // 需要了解所有的具体同事对象
    private ConcreteColleague1 c1;
    private ConcreteColleague2 c2;

    public ConcreteColleague1 getC1() {
    
    
		return c1;
    }
    public void setC1(ConcreteColleague1 c1) {
    
    
		this.c1 = c1;
    }

    public ConcreteColleague2 getC2() {
    
    
		return c2;
    }
    public void setC2(ConcreteColleague2 c2) {
    
    
		this.c2 = c2;
    }

    @Override
    public void send(String message, Colleague colleague) {
    
    
        // 重写发送信息的方法,根据对象做出选择判断,通知对象
        if (colleague == c1) {
    
    
            c2.notifyMsg(message);
        } else {
    
    
            c1.notifyMsg(message);
        }
    }
}
//抽象同事类
public abstract class Colleague {
    
    

    protected Mediator mediator;
    public Colleague(Mediator mediator) {
    
    
		this.mediator = mediator;
    }
    public abstract void sendMsg(String message);
    public abstract void notifyMsg(String message);
}
//具体同事类
class ConcreteColleague1 extends Colleague {
    
    

    public ConcreteColleague1(Mediator mediator) {
    
    
		super(mediator);
    }

    @Override
    public void sendMsg(String message) {
    
    
		mediator.send(message, this);
    }

    @Override
    public void notifyMsg(String message) {
    
    
		System.out.println("同事1得到消息:" + message);
    }

}

class ConcreteColleague2 extends Colleague {
    
    

    public ConcreteColleague2(Mediator mediator) {
    
    
		super(mediator);
    }

    @Override
    public void sendMsg(String message) {
    
    
		mediator.send(message, this);
    }

    @Override
    public void notifyMsg(String message) {
    
    
		System.out.println("同事2得到消息:" + message);
    }
}
public class MediatorClient {
    
    
    public static void main(String[] args) {
    
    
        ConcreteMediator concreteMediator = new ConcreteMediator();

        // 让两个具体同事类认识中介者对象
        ConcreteColleague1 concreteColleague1 = new ConcreteColleague1(
            concreteMediator);
        ConcreteColleague2 concreteColleague2 = new ConcreteColleague2(
            concreteMediator);

        // 让中介者认识各个具体同事类对象
        concreteMediator.setC1(concreteColleague1);
        concreteMediator.setC2(concreteColleague2);

        // 具体同事类对象的消息发送都是通过中介者对象转发
        concreteColleague1.sendMsg("吃过饭了没有?");
        concreteColleague2.sendMsg("没有呢,你打算请客?");
    }
}

Advantages and disadvantages of the intermediary model

advantage:

  • Reduce the coupling between each object instance , and you can independently change and reuse each object instance associated with the mediator
  • Abstract the collaboration between objects and encapsulate the intermediary in the object as an independent concept. In this way, the object of concern shifts from the behavior of the objects themselves to the interaction between them, that is, from a more macro perspective . to look at the system

shortcoming:

  • The advantages of the intermediary model come from centralized control , and the disadvantages also come from centralized control

  • The mediator achieves centralization of control and transforms the complexity of interactions into the complexity of the mediator. When there are more classes, the business of the mediator becomes more complex, and the code becomes difficult to manage and change.

Application of mediator pattern:

The mediator pattern is generally used when a group of objects communicate in a well-defined but complex way, and when you want to customize a behavior that is distributed among multiple classes without generating too many subclasses.

  • The Form of Windows applications in .NET and the aspx of Web website programs all use the intermediary mode.

Summarize

Bridge mode, composition mode and mediator mode all change the relationship between classes or objects , thereby achieving their respective goals of reducing the number of objects or reducing code complexity.

  • The bridge mode uses the principle of composition/aggregation reuse , transforms inheritance into aggregation, separates the abstract part from its implementation part, reduces the number of subclasses, and reduces the coupling of the code.

  • The combination mode uses a tree structure to represent the "part-whole" hierarchy to manage objects, allowing users to use a unified interface to use all objects in the combination structure, simplifying the code and improving scalability.

  • The mediator pattern uses mediators to control and coordinate interactions between a group of objects, converting many-to-many relationships into one-to-many , reducing the coupling between objects and improving code reusability and scalability.

  • The flyweight mode is mainly to solve the problem of memory resource waste. It uses sharing technology to reduce object instances by extracting similar parts between objects.

The key to distinguishing these design patterns still lies in grasping the differences in purpose (intention).

Guess you like

Origin blog.csdn.net/weixin_45483322/article/details/132356812