设计模式之行为型模式(责任链、命令、迭代器、模板、策略、解释器、观察者、状态、中介者)

文章目录

    • 一、行为型设计模式
    • 二、责任链模式
    • 三、命令模式
    • 四、迭代器模式
    • 五、模板模式
    • 六、策略模式
    • 七、解释器模式
    • 八、观察者模式
    • 九、状态模式
    • 十、中介者模式

一、行为型设计模式

这篇文章我们来讲解下行为型设计模式:主要用于描述类或对象之间的交互或职责的分配,为设计类的交互和职责分配做指南。

二、责任链模式

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦

在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

优点:

  1. 降低了耦合度,它将请求的发送者和接收者解耦。
  2. 简化了对象,使得对象不需要知道链的结构。
  3. 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
  4. 增加新的请求处理类很方便。

举个例子:在平时开发中,我们经常需要打印日志来查看一些信息,但打日志时我们都会指定一个日志级别,比如 INFOWARNERROR等,指定不同的级别打印的日志存储的文件是不同的,这种场景就可以使用责任链设计模式来设计解决。

下面使用程序演示下上面的例子:

  1. 定义日志接口
public interface LoggerInterFace {
    //指定下一个目标
    void setNextLogger(LoggerInterFace nextLogger);
    //打印日志
    void logMessage(int level, String message);
    //日志格式
    void writeFormat(String message);
}
  1. 定义日志抽象模板
public abstract class AbstractLogger implements LoggerInterFace {
    public static int INFO = 1;
    public static int WARN = 2;
    public static int ERROR = 3;

    private int level;
    private LoggerInterFace nextLogger;

    public AbstractLogger(int level) {
        this.level = level;
    }

    @Override
    public void setNextLogger(LoggerInterFace nextLogger) {
        this.nextLogger = nextLogger;
    }

    @Override
    public void logMessage(int level, String message) {
        if (this.level == level) {
            writeFormat(message);
        }
        if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }

}
  1. 定义INFO级别的实现
public class InfoLogger extends AbstractLogger {

   public InfoLogger(int level) {
      super(level);
   }

   @Override
   public void writeFormat(String message) {
      System.out.println(StringFormatter.concat(" 普通信息:", message).getValue());
   }

}
  1. 定义WARN级别的实现
public class WarnLogger extends AbstractLogger {

   public WarnLogger(int level) {
      super(level);
   }

   @Override
   public void writeFormat(String message) {
      System.out.println(StringFormatter.concat(" 警告信息:", message).getValue());
   }
}
  1. 定义ERROR级别的实现
public class ErrorLogger extends AbstractLogger {

    public ErrorLogger(int level) {
        super(level);
    }

    @Override
    public void writeFormat(String message) {
        System.out.println(StringFormatter.concat(" 错误信息:", message).getValue());
    }
}
  1. 定义日志工厂,定义责任链的顺序
public class LoggerFactory {

    public static LoggerInterFace getLogger() {
        //定义责任链的顺序
        List<AbstractLogger> list = new LinkedList<>();
        list.add(new ErrorLogger(AbstractLogger.ERROR));
        list.add(new WarnLogger(AbstractLogger.WARN));
        list.add(new InfoLogger(AbstractLogger.INFO));

        for (int i = 0; i < list.size() - 1; i++) {
            list.get(i).setNextLogger(list.get(i + 1));
        }

        return list.get(0);
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        LoggerInterFace logger = LoggerFactory.getLogger();

        logger.logMessage(AbstractLogger.INFO, " 打印普通日志");

        logger.logMessage(AbstractLogger.WARN, " 打印警告日志");

        logger.logMessage(AbstractLogger.ERROR, " 打印错误日志");
    }
}

在这里插入图片描述

三、命令模式

命令模式(Command Pattern)是一种数据驱动的设计模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

优点:降低了系统耦合度,新的命令可以很容易添加到系统中去。

用画各种形状的例子来说,可以采用命令设计模式来设计,绘画不同的形状就将对应形状的实现传递给命令执行器,以一种统一的命令的形式来做不同的操作。可以有效降低耦合性和提高扩展性。

下面使用程序演示下:

  1. 定义形状接口
public interface ShapeInterFace {
    void draw();
}
  1. 定义绘制圆形的实现
public class CircleShape implements ShapeInterFace {
    @Override
    public void draw() {
        System.out.println("画圆形");
    }
}
  1. 定义绘制矩形的实现
public class RectangleShape implements ShapeInterFace {
    @Override
    public void draw() {
        System.out.println("画矩形!");
    }
}
  1. 定义命令接口
public interface CommandInterFace {
    void execute();
}
  1. 定义命令抽象模板
public abstract class CommandAbstract implements CommandInterFace {
    protected ShapeInterFace shape;

    public CommandAbstract(ShapeInterFace shape) {
        this.shape = shape;
    }
}
  1. 定义形状命令的实现
public class ShapeCommand extends CommandAbstract {

    public ShapeCommand(ShapeInterFace shape) {
        super(shape);
    }

    @Override
    public void execute() {
        shape.draw();
    }
}
  1. 定义命令执行器
public class Invoker {
    private CommandInterFace command;

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

    public void executeCommand() {
        command.execute();
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        Invoker invoker = new Invoker();
        invoker.setCommand(new ShapeCommand(new CircleShape()));
        invoker.executeCommand();

        invoker.setCommand(new ShapeCommand(new RectangleShape()));
        invoker.executeCommand();
    }
}

四、迭代器模式

迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示
优点:

  1. 支持以不同的方式遍历一个聚合对象。
  2. 迭代器简化了聚合类。 在同一个聚合上可以有多个遍历。
  3. 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

下面我们使用程序看下迭代器的设计过程:

  1. 定义迭代器接口

public interface Iterator {
   public boolean hasNext();
   public Object next();
}
  1. 定义迭代器的实现

public class NameIterator implements Iterator {
    private int index;
    private String[] names;

    public NameIterator(String[] names) {
        this.names = names;
    }

    @Override
    public boolean hasNext() {
        if (index < names.length) {
            return true;
        }
        return false;
    }

    @Override
    public Object next() {
        if (this.hasNext()) {
            return names[index++];
        }
        return null;
    }
}

  1. 演示
public class demo {
   public static void main(String[] args) {
      String[] names = {"A" , "B" ,"C" , "D"};

      NameIterator iterator = new NameIterator(names);
      while (iterator.hasNext()){
         String name = (String)iterator.next();
         System.out.println(name);
      }
   }
}

设计模式之行为型模式(责任链、命令、迭代器、模板、策略、解释器、观察者、状态、中介者)_第1张图片

五、模板模式

模板模式(Template Pattern),又叫模板方法设计模式,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
优点:有封装不变扩展可变, 公共代码便于维护,行为由父类控制子类负责具体实现。

举个例子:拿画形状的案例来说,比如我们画一个图像之后还要加边框,还要画颜色,如果我们画每个图像都要再考虑怎么加入边框和颜色,就显得有点臃肿,因为加入边框和颜色对所有的图像都是一致的,此时就可以将图形边框和颜色在一个统一的模板中定义规则,具体的实现放在具体的子类中。

下面使用程序演示下上面的例子:

  1. 定义形状的接口,并定义画图形、加边框、画颜色和生成最终的图形
public interface ShapeInterFace {
    void drawShape();

    void drawBorder();

    void drawColor();

    void genShape();
}
  1. 定义模板
public abstract class ShapeTemplate implements ShapeInterFace {
    @Override
    public void genShape() {
        System.out.println("开始整合图形");
        drawShape();
        drawBorder();
        drawColor();
        System.out.println("整合完毕");
    }
}
  1. 定义圆形的实现
public class CircleShapeImpl extends ShapeTemplate {
    @Override
    public void drawShape() {
        System.out.println("画圆形");
    }

    @Override
    public void drawBorder() {
        System.out.println("画圆形的边框");
    }

    @Override
    public void drawColor() {
        System.out.println("画圆形的颜色");
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        ShapeInterFace shape = new CircleShapeImpl();
        shape.genShape();
    }
}

设计模式之行为型模式(责任链、命令、迭代器、模板、策略、解释器、观察者、状态、中介者)_第2张图片

六、策略模式

策略模式(Strategy Pattern),一个类的行为或其算法可以在运行时更改。我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

采用画图形的例子来说,画不同的图形,通过一个标识来做动作,而不是调用具体的类。

下面使用程序演示下上面的例子:

  1. 定义形状接口
public interface ShapeInterFace {
    void draw();
}
  1. 定义圆形的实现
public class CricleShape implements ShapeInterFace {
    @Override
    public void draw() {
        System.out.println("画圆形!");
    }
}
  1. 定义矩形的实现
public class RectangleShape implements ShapeInterFace {
    @Override
    public void draw() {
        System.out.println("画矩形");
    }
}
  1. 定义三角形的实现
public class TriangleShape implements ShapeInterFace {
    @Override
    public void draw() {
        System.out.println("画三角形");
    }
}
  1. 定义形状工厂,根据策略拿到不同的实现
public class FactoryStrategy {

    private static Map<String, ShapeInterFace> strategys = new ConcurrentHashMap<String, ShapeInterFace>();

    static {
        strategys.put("circle", new CricleShape());
        strategys.put("rectangle", new RectangleShape());
        strategys.put("triangle", new TriangleShape());
    }

    public static ShapeInterFace getStrategy(String strategyId) {
       return strategys.get(strategyId);
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        FactoryStrategy.getStrategy("circle").draw();
        FactoryStrategy.getStrategy("rectangle").draw();
        FactoryStrategy.getStrategy("triangle").draw();
    }
}

设计模式之行为型模式(责任链、命令、迭代器、模板、策略、解释器、观察者、状态、中介者)_第3张图片

七、解释器模式

解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。

举个例子,拿SQL解析来看,我们写的一般都是SQL指令然后交给数据库就可以拿到我们想要的数据了,数据库是怎么知道我们查那张表的那些数据呢,其实我们将SQL指令交给数据库后,数据库就在对SQL进行解释,检查语法是否正确,并确定查询的表,查询的字段,和条件等。

下面使用程序演示下上面的例子,这里简单的做下SQL 解析,便于理解这种设计模式:

  1. 定义SQL解析接口
public interface SqlnterFace {
    SqlnterFace interpretation(String sql);
    void execute();
}
  1. 定义SELECT查询的实现
public class SelectSqlImpl implements SqlnterFace {
    String sql;
    String columns;
    String table;
    String condition;

    @Override
    public SqlnterFace interpretation(String sql) {
        System.out.println("开始解释SQL ");
        if (!sql.contains("SELECT")) {
            System.out.println("SQL 指令错误!");
            return this;
        }
        this.sql = sql;
        this.columns = sql.split("FROM")[0].split(" ")[1].trim();
        this.table = sql.split("FROM")[1].split("WHERE")[0].trim();
        this.condition = sql.split("WHERE")[1].trim();
        System.out.println("解释结果: 查询的表为:" + this.table + " 查询的列为:" + this.columns + " 条件:" + this.condition);
        return this;
    }

    @Override
    public void execute() {
        System.out.println("执行SQL: " + this.sql);
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        String sql = "SELECT name,age FROM user WHERE id=1 ";
        SqlnterFace sqlExe = new SelectSqlImpl();
        sqlExe.interpretation(sql).execute();
    }
}

在这里插入图片描述

八、观察者模式

观察者模式,当一个对象被修改时,则会自动通知依赖它的对象。对象间存在一对多关系时就可以选用这个设计模式。
优点:观察者和被观察者是抽象耦合的。

举个例子:做通知类型的业务时,可能需要同时发送短信和邮件,如果说在需要发送的时候,我们就每个都调用一次发送的方法,就显得代码有些冗余了,采用观察者设计模式则可以有效解决此类问题。

下面使用程序演示下上面的例子:

  1. 定义事件监听的接口
public interface MsgListener<T> {
    void msgEvent(T msg);
}
  1. 定义事件bean对象
@Data
@AllArgsConstructor
public class MyMsgEvent {
    private int code;
    private String msg;
}
  1. 定义邮件的监听实现
@Slf4j
@Component
public class MyMailLinstener implements MsgListener<MyMsgEvent> {
    @Override
    public void msgEvent(MyMsgEvent msg) {
        System.out.println("邮件监听触发" + msg.toString());
    }
}
  1. 定义短信的监听实现
@Slf4j
@Component
public class MySMSListenter implements MsgListener<MyMsgEvent> {
    @Override
    public void msgEvent(MyMsgEvent msg) {
        System.out.println("短信监听触发" + msg.toString());
    }
}
  1. 定义消息发布类
@Slf4j
@Component
public class MsgPublic {
    private static List<MsgListener> list = new ArrayList<>();

    static  {
        list.add(new MyMailLinstener());
        list.add(new MySMSListenter());
    }

    public static void publicMsg(Object o) {
        list.forEach(listener -> listener.msgEvent(o));
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        MyMsgEvent event = new MyMsgEvent(200, "success");
        MsgPublic.publicMsg(event);
    }
}

在这里插入图片描述

扩展
上面的在消息发布类中时写死的目前有哪些实现,如果不写死可以使用Spring来获取MsgListener下的所有子类来发送信息:

@Slf4j
@Component
public class MsgPublic implements ApplicationRunner, ApplicationContextAware {
    private static ApplicationContext applicationContext;
    private List<MsgListener> list = new ArrayList<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(this.applicationContext == null) {
            this.applicationContext = applicationContext;
        }
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Map<String, MsgListener> beanOfType = applicationContext.getBeansOfType(MsgListener.class);
        beanOfType.forEach((key,val) -> {
            list.add(val);
        });
        log.info("注册监听数:"+beanOfType.size());
    }

    public void publicMsg(Object o) {
        list.forEach(listener -> {
            listener.msgEvent(o);
        });
    }
}

另外,在Spring中,就有这种封装ApplicationListener来便于我们使用,下面看下Spring的使用:

  1. 定义消息bean
public class MsgInfo extends ApplicationEvent {
    private int code;
    private String msg;

    public MsgInfo(Object source) {
        super(source);
    }

    public MsgInfo(Object source, int code, String msg) {
        super(source);
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    @Override
    public String toString() {
        return "MsgInfo{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                '}';
    }
}
  1. 定义短信监听
@Slf4j
@Component
public class SMSListener implements ApplicationListener<MsgInfo> {

    @Override
    public void onApplicationEvent(MsgInfo msgInfo) {
        log.info("短信监听触发" + msgInfo.toString());
    }
}
  1. 定义邮件监听
@Slf4j
@Component
public class EmailListener implements ApplicationListener<MsgInfo> {
    @Override
    public void onApplicationEvent(MsgInfo msgInfo) {
        log.info("邮件监听触发" + msgInfo.toString());
    }
}
  1. 演示
@RestController
public class SendTest {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @GetMapping("/TestPublish")
    public String testPublish() {
        applicationEventPublisher.publishEvent(new MsgInfo(this, 200, "abc"));
        return "success";
    }
}

九、状态模式

状态模式(State Pattern),类的行为是基于它的状态改变的。我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

举个例子,在用户登录状态方面,有登录状态,登出状态等,在登录和登出时,我们不仅要改变状态还要做一些附加的操作,那如果我们修改状态时,就能自动触发附加操作,那岂不是代码的维护性更高了,采用状态模式就可以解决这类问题。

下面使用程序演示下上面的例子:

  1. 定义状态接口
public interface StateInterFace {
    void login(Context context);
}
  1. 定义登录状态的实现
public class LoginStatus implements StateInterFace {
    @Override
    public void login(Context context) {
        System.out.println("登录的附加操作!");
        System.out.println("已经切换到登陆状态");
    }
}
  1. 定义登出状态的实现
public class LoginOutStatus implements StateInterFace {
    @Override
    public void login(Context context) {
        System.out.println("登出附加操作!");
        System.out.println("已切换到登出状态!");
    }
}
  1. 定义Context
public class Context {
    public static final StateInterFace LOGIN_STATUS = new LoginStatus();
    public static final StateInterFace LOGINOUT_STATUS = new LoginOutStatus();
    public static final StateInterFace DEFAULT_STATUS = LOGIN_STATUS;


    public void setState(StateInterFace state) {
        state.login(this);
    }
}
  1. 演示
public class demo {
    public static void main(String[] args) {
        Context context = new Context();
        context.setState(Context.LOGIN_STATUS);
        context.setState(Context.LOGINOUT_STATUS);
    }
}

设计模式之行为型模式(责任链、命令、迭代器、模板、策略、解释器、观察者、状态、中介者)_第4张图片

十、中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
优点:为了降低类的复杂度,将一对多转化成了一对一,各个类之间的解耦,符合迪米特原则。

举个例子:比如我们发送消息,我们肯定要调用发送消息的封装,肯定需要传递消息内容和消息的接收人信息这两个参数吧,但问题来了,我要发给小明要把消息接收人指定为小明的人员信息,要发给小红要把消息接收人指定为小红的人员信息。现在我们就像既然我都拿到接受人的信息了,为什么不能直接给他发送呢,反而多了一步调用发消息的封装。采用中介者模式的思想,我们可以在每个人员信息的类中去调用发消息的封装,就省去了我们多的哪一步,此时中介者就是人员信息类。

下面使用程序演示下上面的例子:

  1. 定义发送消息的类
public class ChatRoom {
    public static void showMessage(User user, String message) {
        System.out.println("发送信息:" + message + ", 接收人:" + user);
    }
}
  1. 定义人员信息类
@Data
@AllArgsConstructor
public class User {
   private String name;
 
   public void sendMessage(String message){
      ChatRoom.showMessage(this,message);
   }
}
  1. 演示
public class demo {
   public static void main(String[] args) {
      User robert = new User("小明");
      User john = new User("小红");
 
      robert.sendMessage("哈哈哈");
      john.sendMessage("嗯嗯");
   }
}

在这里插入图片描述

你可能感兴趣的:(实用工具及技术篇,Java之旅,设计模式,java)