美文网首页
设计模式

设计模式

作者: 乔治m2 | 来源:发表于2022-04-25 18:55 被阅读0次

设计模式

好的代码离不开设计模式,我们在查看一些优秀的项目或者框架时都会找到设计模式的影子。 在软件模式中,设计模式是研究最为深入的分支,设计模式用于在特定的条件下为一些重复出现的软件设计问题提供合理的、有效的解决方案,它融合了众多专家的设计经验,已经在成千上万的软件中得以应用。

以下对设计模式进行简要的说明和使用,更详细的用法还需自己动手实践。

一、工厂类设计模式

1、简单工厂

简单工厂模式可以理解给定一个type,根据这个type判断生成相应的对象。

public class Factory() {
    public static Base getInstatce(String type) {
        if (type == "A") {
            return A;
        } else if (type == "B") {
            return B;
        }
    }
}

2、工厂模式

相比与简单工厂,工厂模式专门为每个类生成了一个工厂类,通过调用工厂类的静态方法,获取相应的对象。

public class BaseFactory{
    public static BaseClass getInstance() {
        return new BaseClass;
    }
}

public class SubFactory{
    public static SubClass getInstance() {
        return new SubClass();
    }
}

3、抽象工厂模式

相比于工厂模式,抽象工厂模式可以通过工厂类获取归于一类的对象。比如电器,分格力电器,海尔电器,那么通过格力工厂就可以获取到格力空调,格力电视,格力冰箱等等。

public class GeLiFactory{
    public static GeLiTelevision getTV() {
        return new GeLiTelevision();
    }
    
    public static GeLiAirConditioner getAirCond() {
        return new GeLiAirConditioner();
    }
    
    public static GeLiRefrigerator getRefirgerator() {
        return new GeLiRefrigerator();
    }
}

public class HairFactory() {
    public static HairTelevision getTV() {
        return new HairTelevision();
    }
    
    public static HairAirConditioner getAirCond() {
        return new HairAirConditioner();
    }
    
    public static HairRefrigerator getRefirgerator() {
        return new HairRefrigerator();
    }
}

二、单例模式

1、懒汉式

懒汉式单例是需要的时候才进行初始化,因此在第一次调用的时候,要进行双重校验,防止多线程模式下,破坏单例。

public class Singleton{
    
    private final static volatile Singleton instance; 
    
    private Singleton(){};
    
    public Singleton getInstance() {
        if (Singleton == null) {
            synchronized(Singleton.class) {
                if(Singleton == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2、饿汉式

饿汉式相比于懒汉式来说,单例对象在项目启动的时候就进行了初始化,不能实现延迟加载。

public class Singleton{
    
    private final static Singleton instance = new Singleton(); 
    
    private Singleton(){};
    
    public Singleton getInstance() {
        return instance;
    }
}

3、loDH

通过静态内部类实现单例,既可以避免锁的影响,又可以实现延迟加载。

public class Singleton{
    private Singleton(){};
    
    private static class HolderClass{
        private final static Singleton instance = new Singleton();
    } 
    
    public static Singleton getInstance() {
        return HolderClass.instance;
    }
}

4、枚举

最好的单例是枚举,相比于其他单例模式,枚举还可避免反射破坏单例。

public enum EnumSingleton{
    APPLE,ORANGE;
}

三、建造者模式

建造者模式重点在于内部builder类。通过builder类一步一步的设置参数。

public class LogInfo {


    /**
     * 资源id
     */
    private String resNum;

    /**
     * 操作类型:1-增加,2-删除,3-修改
     */
    private Integer action;

    /**
     * 操作内容
     */
    private String content;

    /**
     * 操作人名称
     */
    private String opName;

    /**
     * 操作人工号
     */
    private String opAccount;

    /**
     * 操作时间
     */
    private Timestamp opTime;

    /**
     * 操作备注
     */
    private String opDesc;

    public LogInfo() {}

    private LogInfo(LogBuilder builder) {
        this.resNum = builder.resNum;
        this.action = builder.action;
        this.content = builder.content;
        this.opDesc = builder.opDesc;
    }

    public static class LogBuilder{
        private String resNum;

        private Integer action;

        private String content;

        private String opDesc;

        public LogBuilder(String resNum, Integer action) {
            this.resNum = resNum;
            this.action = action;
        }

        public LogBuilder content(String content) {
            this.content = content;
            return this;
        }

        public LogBuilder opDesc(String opDesc) {
            this.opDesc = opDesc;
            return this;
        }

        public LogInfo build() {
            return new LogInfo(this);
        }
    }

四、组合模式

组合模式用的最多的是安全组合模式,分别包括叶子节点(只含基本方法),容器(含基本方法和对象存储的list),叶子节点和容器需要实现同一个接口。展现多态的优势。

组件接口

public interface Component {

    void show();
}

叶子节点1:

public class Left1 implements Component{

    @Override
    public void show() {
        System.out.println("我是叶子节点");
    }
}

叶子节点2:

public class Left2 implements Component{
    @Override
    public void show() {
        System.out.println("我是另外一个叶子节点...");
    }
}

容器:

public class Container implements Component{

    List<Component> list = new ArrayList<>();

    public void add(Component c) {
        list.add(c);
    }

    public void remove(Component c) {
        list.remove(c);
    }

    @Override
    public void show() {
        System.out.println("容器>>>>");
        for (Component c : list) {
            c.show();
        }
    }

测试:

    public static void main(String[] args) {
        Container container1 = new Container();
        Container container2 = new Container();
        Container container3 = new Container();
        Left1 left1 = new Left1();
        Left2 left2 = new Left2();
        container2.add(left1);
        container3.add(left2);
        container1.add(container2);
        container1.add(container3);
        container1.show();
    }

五、装饰者模式

装饰者模式可以理解为对一个对象的方法进行扩展,增加 一些新的功能。

优点:

1、对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

2、可以对一个对象进行多次装饰。

缺点:

1、对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

组件:

public interface Component {

    void display();
}

抽象装饰器:

// 抽象装饰类
public class Decorator implements Component{

    private Component component;

    public Decorator(Component component) {
        this.component = component;
    }

    @Override
    public void display() {
        component.display();
    }
}

具体装饰器:

// 具体装饰器
public class ConcreteDecorator extends Decorator{

    public ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void display() {
        super.display();
        addBehavior();
    }

    public void addBehavior() {
        System.out.println("装饰一下");
    }
}

物品:

public class House implements Component{

    @Override
    public void display() {
        System.out.println("房子.....");
    }
}

测试:

public class Main {

    public static void main(String[] args) {
        House house = new House();
        ConcreteDecorator newHouse = new ConcreteDecorator(house);
        newHouse.display();
    }
}

六、适配器模式

适配器模式从代码层面可以理解为当前类无法直接调用此方法,需要通过适配器进行间接调用。

优点:

1、将目标类和适配者类解耦。

2、灵活性和扩展性都非常好。

缺点:

1、不能同时适配多个适配者。

2、类适配器模式中的目标抽象类只能为接口,不能为类。

这里通过适配器对快排和二分查找源码进行适配,使用者可直接调用适配器中的排序和查找方法即可。

操作接口:

public interface Operation {

    void quickSort(int[] arrays);

    int binarySearch(int[] arrays, int key);
}

快排类:

public class QuickSort {

    public int partition(int[] array, int low, int high) {
        int base = array[low];
        while (low < high) {
            while (low < high && array[high] >= base) {
                high--;
            }
            if (low < high) {
                array[low] = array[high];
            }
            while (low < high && array[low] >= base) {
                low++;
            }
            if (low < high) {
                array[high] = array[low];
            }
        }
        array[low] = base;
        return low;
    }

    public  void quickSort(int[] array, int low, int high) {
        if (low < high) {
            // 获取划分子数组的位置
            int position = partition(array, low, high);
            // 左子数组递归调用
            quickSort(array, low, position - 1);
            // 右子数组递归调用
            quickSort(array, position + 1, high);
        }
    }
}

二分查找类:

public class BinarySearch {

    public int binary(int[] arrays, int key) {
        int low = 0;
        int high = arrays.length - 1;
        while (low < high) {
            int mid = (low + high) / 2;
            int midValue = arrays[mid];
            if (midValue < key) {
                low = mid + 1;
            } else if (midValue > key) {
                high = mid - 1;
            } else {
                return 1;
            }
        }
        return -1;
    }
}

适配器:

public class OperationAdapter implements Operation{

    private QuickSort quickSort;

    private BinarySearch binarySearch;

    public OperationAdapter() {
        this.quickSort = new QuickSort();
        this.binarySearch = new BinarySearch();
    }

    @Override
    public void quickSort(int[] arrays) {
        quickSort.quickSort(arrays, 0, arrays.length - 1);
    }

    @Override
    public int binarySearch(int[] arrays, int key) {
        return binarySearch.binary(arrays, key);
    }
}

七、策略模式

策略模式的作用主要是将算法和判断分割开,实现解耦。如果代码中有大量的if..else,可以使用策略模式进行优化。

优点:

1、用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。

2、策略模式提供了管理相关的算法族的办法。

3、策略模式可以避免多重条件选择语句。

4、策略模式提供了一种算法的复用机制。

缺点:

1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

2、策略模式将造成系统产生很多具体策略类。

3、无法同时在客户端使用多个策略类。

没有使用策略模式业务逻辑:

以下代码有两个主要问题,一是不符合**开闭原则**,可以预见,如果后续新增服务方法的话,需要直接修改主干代码,而我们提倡代码应该是对修改封闭的;二是不符合**迪米特法则**,业务逻辑和各个下游接口高度耦合,这导致接口的改变将直接影响到代码的组织,使得代码的可维护性降低。 
public static void deal(String type) {
        if (type.equals("a")) {
            System.out.println("other operation");
            AService aService = new AService();
            aService.run();
        } else if (type.equals("b")) {
            System.out.println("other operation");
            BService bService = new BService();
            bService.run();
        } else if (type.equals("c")) {
            System.out.println("other operation");
            CService cService = new CService();
            cService.run();
        }
    }

使用策略模式:
项目结构:

|-service(业务服务方法)
|---AService
|---Bservice
|---Cservice
|-strategy(策略方法)
|---AbstractStrategy
|---AServiceStrategy
|---BServiceStrategy
|---CServiceStrategy
|---Strategy
|---StrategyContext

service包的下的业务方法:

public class AService{

    public void run() {
        System.out.println("A service");
    }
}
public class BService {

    public void run() {
        System.out.println("B service");
    }
}
public class CService {

    public void run() {
        System.out.println("C service");
    }
}

strategy包下的方法:

// 抽象策略方法
public interface Strategy {

    void issue();
}

// 策略上下文,用于管理策略的注册和获取
public class StrategyContext {

    public static final Map<String, Strategy> registerMap = new HashMap<>();
    // 注册策略
    public static void register(String name, Strategy strategy) {
        registerMap.put(name, strategy);
    }
    // 获取策略
    public static Strategy getStrategy(String name) {
        return registerMap.get(name);
    }
}

// 抽象策略类
public abstract class AbstractStrategy implements Strategy {
    // 类注册方法
    public void register(String type) {
        StrategyContext.register(type, this);
    }
}

// 单例策略类A
public class AServiceStrategy extends AbstractStrategy implements Strategy {

    private static final AbstractStrategy strategy = new AServiceStrategy("A");

    private AServiceStrategy(String type) {
        register(type);
    }

    @Override
    public void issue() {
        System.out.println("other operation");
        AService aService = new AService();
        aService.run();
    }
}

// 单例策略类B
public class BServiceStrategy extends AbstractStrategy implements Strategy {

    public static final BServiceStrategy strategy = new BServiceStrategy("B");

    private BServiceStrategy(String type) {
        register(type);
    }

    @Override
    public void issue() {
        System.out.println("other operation");
        BService bService = new BService();
        bService.run();
    }
}
// 单例策略类C
public class CServiceStrategy extends AbstractStrategy implements Strategy {

    private static final CServiceStrategy strategy = new CServiceStrategy("C");

    public CServiceStrategy(String type) {
        register(type);
    }

    @Override
    public void issue() {
        System.out.println("other operation");
        CService cService = new CService();
        cService.run();
    }
}

八、观察者模式

观察者模式可理解为一个对象状态发生了改变,会使其他的观察者对象收到通知,并做出相应的反应。

如顾客(具体的观察者)和商品(具体的主题),一个商品被多个顾客收藏了,当商品的价格发生变化的时候,就会通知顾客。

抽象观察者:

public interface Observer {
    void update();
}

具体观察者(如员工):

public class ConcreteObserver implements Observer{
    @Override
    public void update() {
        System.out.println("监控到对象发生变化");
    }
}

抽象观察对象:

public abstract class Subject {

    protected List<Observer> observers = new ArrayList<>();

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void detach(Observer observer) {
        observers.remove(observer);
    }

    public abstract void notifyObserver();
}

具体观察对象(某个具体的主题,如商品、事物的状态):

public class ConcreteSubject extends Subject{
    @Override
    public void notifyObserver() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

测试:

public class Main {

    public static void main(String[] args) {
        ConcreteSubject subject1 = new ConcreteSubject();
        subject1.attach(new ConcreteObserver());
        subject1.attach(new ConcreteObserver());
        subject1.notifyObserver();
    }
}

九、状态模式

状态模式:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。状态模式包含以下主要角色:

  • 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

当我们的业务中存在状态流转的时候,这是可以考虑状态模式

没有使用状态模式的代码

// 任务状态枚举
public enum StatusEnum {
    INIT("初始化"),
    ONGOING("进行中"),
    PAUSED("暂停中"),
    FINISHED("已完成"),
    EXPIRED("已过期");

    private final String message;

    StatusEnum(String msg) {
        this.message = msg;
    }

    public String getMessage() {
        return message;
    }
}
// 行为状态枚举
public enum ActionTypeEnum {
    START(1, "开始"),
    STOP(2, "暂停"),
    ACHIEVE(3, "完成"),
    EXPIRE(4, "过期");

    private final int code;

    private final String message;

    ActionTypeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }
}

// 任务实体类
public class Task {

    private Long taskId;

    // 任务的默认状态为初始化
    private StatusEnum state = StatusEnum.INIT;

    // 使用条件分支进行任务更新
    public void updateState(ActionTypeEnum actionType) {
        if (state == StatusEnum.INIT) {
            if (actionType == ActionTypeEnum.START) {
                state = StatusEnum.ONGOING;
            }
        } else if (state == StatusEnum.ONGOING) {
            if (actionType == ActionTypeEnum.ACHIEVE) {
                state = StatusEnum.FINISHED;
                // 任务完成后进对外部服务进行通知
                System.out.println("活动管理器通知:任务处理完成" + taskId);
                System.out.println("任务管理器处理" + taskId);
            } else if (actionType == ActionTypeEnum.STOP) {
                state = StatusEnum.PAUSED;
            } else if (actionType == ActionTypeEnum.EXPIRE) {
                state = StatusEnum.EXPIRED;
            }
        } else if (state == StatusEnum.PAUSED) {
            if (actionType == ActionTypeEnum.START) {
                state = StatusEnum.ONGOING;
            } else if (actionType == ActionTypeEnum.EXPIRE) {
                state = StatusEnum.EXPIRED;
            }
        }
    }
}

以上方法存在一下问题: 第一,方法中使用条件判断来控制语句,但是当条件复杂或者状态太多时,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的if-else语句,这违背了开闭原则,不利于程序的扩展。 第二,任务类不够高内聚,它在通知实现中感知了其他领域或模块的模型,如活动和任务管理器,这样代码的耦合度太高,不利于扩展。

根据状态模式的定义,将TaskState枚举类扩展成多个状态类,并具备完成状态的流转的能力;然后优化了任务类的实现:

// 任务状态抽象接口
public interface Status {

    void update(Task task, ActionTypeEnum actionType);
}

// 任务初始状态
public class TaskInit implements Status{

    @Override
    public void update(Task task, ActionTypeEnum actionType) {
        if  (actionType == ActionTypeEnum.START) {
            task.setStatus(new TaskOnGoing());
        }
    }
}

// 任务进行状态
public class TaskOnGoing implements Status{
    @Override
    public void update(Task task, ActionTypeEnum actionType) {
        if (actionType == ActionTypeEnum.ACHIEVE) {
            task.setStatus(new TaskFinished());
            System.out.println("通知" + task.getTaskId());
        } else if (actionType == ActionTypeEnum.STOP) {
            task.setStatus(new TaskPaused());
        } else if (actionType == ActionTypeEnum.EXPIRE) {
            task.setStatus(new TaskExpired());
        }
    }
}

// 任务暂停状态
public class TaskPaused implements Status{
    @Override
    public void update(Task task, ActionTypeEnum actionType) {
        if (actionType == ActionTypeEnum.START) {
            task.setStatus(new TaskOnGoing());
        } else if (actionType == ActionTypeEnum.EXPIRE) {
            task.setStatus(new TaskExpired());
        }
    }
}

// 任务完成状态
public class TaskFinished implements Status{
    @Override
    public void update(Task task, ActionTypeEnum actionType) {
    }
}

// 任务过期状态
public class TaskExpired implements Status{
    @Override
    public void update(Task task, ActionTypeEnum actionType) {
    }
}

// 任务类
public class Task {

    private Long taskId;

    private Status status = new TaskInit();

    public void update(ActionTypeEnum actionTypeEnum) {
        status.update(this, actionTypeEnum);
    }

    public Long getTaskId() {
        return taskId;
    }

    public void setTaskId(Long taskId) {
        this.taskId = taskId;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
    }
}

十、代理模式

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问

代理模式的主要角色如下:

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

抽象主题类:

public interface MyService {
    // 需要被代理的方法
    void dealUserQuestion();
}

真实主题类:

public class MyRealService implements MyService{
    
    @Override
    public void dealUserQuestion() {
        System.out.println("处理用户的方法");
    }
}

代理类:

public class MyProxy implements MyService{

    private MyService myService;

    @Override
    public void dealUserQuestion() {
        if (myService == null) {
            myService = new MyRealService();
        }
        preProxy();
        myService.dealUserQuestion();
        postProxy();
    }

    private void preProxy() {
        System.out.println("代理之前");
    }

    private void postProxy() {
        System.out.println("代理之后");
    }
}

1、JDK动态代理

上面的例子展示的是静态代理,其实代理模式的运用很多,下面将展示常用的JDK动态代理。

数据服务接口:

/**
 * 数据服务
 */
public interface DataService {

    void dealData();
}

数据服务接口实现类Ⅰ:

public class FileDataService implements DataService{

    @Override
    public void dealData() {
        System.out.println("文件数据服务!");
    }
}

数据服务接口实现类Ⅱ:

public class TextDataService implements DataService{

    @Override
    public void dealData() {
        System.out.println("文本数据服务!");
    }
}

JDK动态代理类:

public class DataDynamicProxy implements InvocationHandler {

    private DataService target;

    public DataService getInstance(DataService dataService) {
        this.target = dataService;
        Class<? extends DataService> clazz = this.target.getClass();
        return (DataService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("数据服务代理前......");
    }

    private void after() {
        System.out.println("数据服务代理后......");
    }
}

测试方法:

public class Main {

    public static void main(String[] args) {
        DataDynamicProxy proxy = new DataDynamicProxy();
        DataService fileProxy = proxy.getInstance(new FileDataService());
        fileProxy.dealData();
        DataService textProxy = proxy.getInstance(new TextDataService());
        textProxy.dealData();
    }
}
输出:
数据服务代理前......
文件数据服务!
数据服务代理后......
数据服务代理前......
文本数据服务!
数据服务代理后......

十一、桥接模式

桥接(Bridge)模式的定义如下:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。

桥接(Bridge)模式包含以下主要角色。

  • 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。(如毛笔)
  • 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。(如毛笔的种类:软毛、硬毛)
  • 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。(如毛笔的颜色)
  • 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。(毛笔颜色:红色、绿色)

桥接(Bridge)模式的优点是:

  • 抽象与实现分离,扩展能力强
  • 符合开闭原则
  • 符合合成复用原则
  • 其实现细节对客户透明

缺点是:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。

对于毛笔而言,由于型号是其固有的维度,因此可以设计一个抽象的毛笔类,在该类中声明并部分实现毛笔的业务方法,而将各种型号的毛笔作为其子类;颜色是毛笔的另一个维度,由于它与毛笔之间存在一种“设置”的关系,因此我们可以提供一个抽象的颜色接口,而将具体的颜色作为实现该接口的子类。在此,型号可认为是毛笔的抽象部分,而颜色是毛笔的实现部分。

实现类接口:

/**
 * 毛笔颜色
 * 实现类接口
 */
public interface BrushColorImpl {

    void Color();
}

// 具体的实现类
public class RedBrushColor implements BrushColorImpl{

    @Override
    public void Color() {
        System.out.println("红色");
    }
}

public class YellowBrushColor implements BrushColorImpl{
    @Override
    public void Color() {
        System.out.println("黄色");
    }
}

public class GreenBrushColor implements BrushColorImpl{

    @Override
    public void Color() {
        System.out.println("绿色");
    }
}

抽象类:

/**
 * 抽象类
 * 毛笔
 */
public abstract class Brush {

    protected BrushColorImpl brushColor;

    public void setBrushColor(BrushColorImpl brushColor) {
        this.brushColor = brushColor;
    }

    protected abstract void drown();
}

/**
 * 软性毛笔
 */
public class SoftBrush extends Brush{

    @Override
    public void drown() {
        System.out.println("笔的颜色");
        brushColor.Color();
        System.out.println("进行书写");
    }
}

/**
 * 硬性毛笔
 */
public class HardBrush extends Brush{

    @Override
    public void drown() {
        System.out.println("笔的颜色");
        brushColor.Color();
        System.out.println("开始书写");
    }
}

测试:

public class Main {

    public static void main(String[] args) {
        RedBrushColor color = new RedBrushColor();
        SoftBrush softBrush = new SoftBrush();
        softBrush.setBrushColor(color);
        softBrush.drown();
    }
}

输出:
笔的颜色
红色
进行书写

十二、命令模式

命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

命令模式包含以下主要角色。

  • 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  • 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
  • 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  • 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

抽象命令类:

// 抽象命令类
public abstract class Command {

    abstract void execute();
}

具体命令类:

// 删除命令
public class DeleteCommand extends Command{

    private Receiver receiver;

    public void setReceiver(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    void execute() {
        System.out.println("删除命令被调用");
        receiver.action();
    }
}

调用者:

// 命令调用者
public class Invoker {

    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    public void setCommand(Command command) {
        this.command = command;
    }

    public void call() {
        System.out.println("命令请求者发送命令");
        this.command.execute();
    }
}

接收者:

// 命令接收者
public class Receiver {

    public void action() {
        System.out.println("命令接收者接收到命令");
    }
}

测试:

public class Main {

    public static void main(String[] args) {
        DeleteCommand command = new DeleteCommand();
        command.setReceiver(new Receiver());
        Invoker invoker = new Invoker(command);
        invoker.call();
    }
}
输出:
命令请求者发送命令
删除命令被调用
命令接收者接收到命令

例子:计算操作回滚

// 加法对象
public class Adder {

    private int num;

    public int add(int value) {
        num += value;
        return num;
    }
}

// 抽象命令类
public abstract class AbstractCommand {

    abstract int execute(int value);

    abstract int undo();
}

// 具体命令类
public class ConcreteCommand extends AbstractCommand{

    private Adder adder = new Adder();
    private Stack<Integer> stack = new Stack<>();

    @Override
    int execute(int value) {
        int result = adder.add(value);
        stack.push(value);
        return result;
    }

    @Override
    int undo() {
        if (!stack.isEmpty()) {
            int res = adder.add(-stack.pop());
            return res;
        }
        return 0;
    }
}

// 计算器对象,命令请求者
public class CalculatorForm {

    private AbstractCommand command;

    public void setCommand(AbstractCommand command) {
        this.command = command;
    }

    public void compute(int value) {
        int res = command.execute(value);
        System.out.println("计算结果为: " + res);
    }

    public void undo() {
        int res = command.undo();
        System.out.println("回滚后的结果为: " + res);
    }
}

// 测试类
public class Main {

    public static void main(String[] args) {
        ConcreteCommand command = new ConcreteCommand();
        CalculatorForm calculatorForm = new CalculatorForm();
        calculatorForm.setCommand(command);
        calculatorForm.compute(11);
        calculatorForm.compute(11);
        calculatorForm.compute(5);
        calculatorForm.undo();
        calculatorForm.undo();
    }
}

输出结果:
计算结果为: 11
计算结果为: 22
计算结果为: 27
回滚后的结果为: 22
回滚后的结果为: 11

相关文章

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 设计模式笔记汇总

    目录 设计原则 “依赖倒置”原则 未完待续... 设计模式 设计模式——策略模式 设计模式——装饰者模式 设计模式...

  • 设计模式

    《C#设计模式》 《C#设计模式》-设计模式概述 《C#设计模式》-面向对象设计原则 《C#设计模式》-单例模式 ...

  • 浅谈JS的一些设计模式

    @(书籍阅读)[JavaScript, 设计模式] 常见设计模式 设计模式简介 设计模式概念解读 设计模式的发展与...

  • 前端设计模式

    JS设计模式一:工厂模式jS设计模式二:单例模式JS设计模式三:模块模式JS设计模式四:代理模式JS设计模式五:职...

  • 设计模式之工厂模式

    设计模式之工厂模式 标签(空格分隔): 设计模式 工厂模式 设计模式的感念 设计模式的应用 工厂设计模式的产生 工...

  • JavaJavascript基础进阶(十七)JS中常用的设计模式

    单利设计模式、构造原型设计模式、发布订阅设计模式、promise设计模式 单利模式 构造原型设计模式 最贴近OOP...

  • 设计模式 - 目录

    设计模式01 - 单例模式 设计模式02 - 工厂模式 设计模式03 - 建造者模式 设计模式04 - 适配器模式...

  • 第1章 设计模式概述

    一、设计模式的概念 二、设计模式的历史 三、设计模式的要素 四、设计模式的分类 ■ 创建型设计模式 ■ 结构型设计...

  • iOS设计模式(3)适配器模式

    设计模式系列文章 《iOS设计模式(1)简单工厂模式》《iOS设计模式(2)工厂模式》《iOS设计模式(4)抽象工...

网友评论

      本文标题:设计模式

      本文链接:https://www.haomeiwen.com/subject/pxhqyrtx.html