设计模式 行为型

行为型模式

模板方法模式

模板方法模式定义了一个流程的骨架,由多个方法组成。并允许子类为一个或多个步骤提供实现。简而言之就是公共的不变的部分由父类统一实现,变化的部分由子类来个性化实现。

优点:

  1. 提高复用性;
  2. 提高拓展性;
  3. 符合开闭原则。

缺点:

  1. 类的数目增加;
  2. 增加了系统实现的复杂度;
  3. 父类添加新的抽象方法,所有子类都要改一遍。

举个模板方法模式的例子。定义一个外卖的接口,包含下单、制作和打包配送三个步骤:

public abstract class Takeaway {

    final void order() {
        System.out.println("下单");
    }

    final void packageSend() {
        System.out.println("打包派送");
    }

    protected abstract void make();

    protected boolean needTableware() {
        return true;
    }

    final void flow() {
        this.order();
        this.make();
        if (needTableware()) {
            System.out.println("赠送一次性餐具");
        }
        this.packageSend();
    }
}

其中下单和打包配送行为是固定的,不同的是制作过程,所以orderpackageSend方法提供了默认实现,并且由final修饰,子类不可重写。此外,我们还可以通过needTableware这个钩子方法来控制某些子类的定制化需求。

新增BarbecueTakeaway继承Takeaway:

public class BarbecueTakeaway extends Takeaway {

    private final boolean needTableware;

    public BarbecueTakeaway(boolean needTableware) {
        this.needTableware = needTableware;
    }

    @Override
    protected void make() {
        System.out.println("制作烤肉");
    }

    @Override
    protected boolean needTableware() {
        return this.needTableware;
    }
}

新增FruitTakeaway继承Takeaway:

public class FruitTakeaway extends Takeaway {

    @Override
    protected void make() {
        System.out.println("水果配货");
    }

    @Override
    protected boolean needTableware() {
        return false;
    }
}

新增个客户端Application测试:

public class Application {

    public static void main(String[] args) {
        Takeaway barbecue = new BarbecueTakeaway(true);
        barbecue.flow();

        FruitTakeaway fruit = new FruitTakeaway();
        fruit.flow();
    }
}
下单
制作烤肉
赠送一次性餐具
打包派送
下单
水果配货
打包派送

UML:

QQ20200421-153516@2x

迭代器模式

ignore

策略模式

策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化不会影响到使用算法的用户。策略模式常用于消除大量的if else代码。

适用场景:

  1. 系统有很多类,它们的区别仅仅在于行为不同;
  2. 一个系统需要动态地在几种算法中选择一种;

举个策略模式的例子(促销活动),定义一个促销策略接口:

public interface PromotionStrategy {

    void promotion();
}

实现类之一(策略之一),满减促销策略:

public class FullReductionPromotionStrategy implements PromotionStrategy {
    public void promotion() {
        System.out.println("满1000立减1");
    }
}

实现类之一(策略之一),打折促销策略:

public class DiscountPromotionStrategy implements PromotionStrategy {
    public void promotion() {
        System.out.println("9.9折钜惠");
    }
}

创建一个客户端测试:

public class Application {

    public static void main(String[] args) {
        // 模拟客户端传递的促销策略key
        String promotionKey = "fr";
        PromotionStrategy strategy;
        if ("fr".equals(promotionKey)) {
            strategy = new FullReductionPromotionStrategy();
        } else if ("ds".equals(promotionKey)) {
            strategy = new DiscountPromotionStrategy();
        } else {
            throw new RuntimeException("暂不支持该促销活动");
        }
        strategy.promotion();
    }
}

输出结果:

满1000立减1

策略模式常结合工厂模式来消除大量的if else代码,我们新建一个促销策略的创建工厂:

public class PromotionStrategyFactory {

    private static final Map<String, PromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();

    private static final PromotionStrategy NON_PROMOTION = () -> System.out.println("无促销活动");

    static {
        PROMOTION_STRATEGY_MAP.put(PromotionKey.FR, new FullReductionPromotionStrategy());
        PROMOTION_STRATEGY_MAP.put(PromotionKey.DS, new DiscountPromotionStrategy());
    }

    private PromotionStrategyFactory() {
    }

    public static PromotionStrategy getPromotionStrategy(String promotionKey) {
        PromotionStrategy strategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
        return strategy == null ? NON_PROMOTION : strategy;
    }

    private interface PromotionKey {
        String FR = "fr";
        String DS = "ds";
    }
}

上面代码中,我们通过Map来装载促销策略,这样可以减少对象的重复创建。如果不希望在static块中一次性初始化所有促销策略,我们可以结合享元模式来推迟对象创建过程。

通过这个工厂方法,上面客户端代码可以简写为:

public class Application {

    public static void main(String[] args) {
        // 模拟客户端传递的促销策略key
        String promotionKey = "fr";
        PromotionStrategy promotionStrategy = PromotionStrategyFactory.getPromotionStrategy(promotionKey);
        promotionStrategy.promotion();
    }
}

解释器模式

用的较少,暂不记录。

观察者模式

观察者模式定义了对象之间的一对多依赖,让多个观察者同时监听某个主题对象,当主体对象发生变化时,它的所有观察者都会收到响应的通知。

优点:

  1. 观察者和被观察者之间建立一个抽象的耦合;
  2. 观察者模式支持广播通信。

缺点:

  1. 观察者之间有过多的细节依赖,提高时间消耗及程序复杂度;
  2. 应避免循环调用。

JDK对观察者模式提供了支持。下面举个观察者模式的例子。

创建一个博客类:

/**
 * 继承 Observable类,Blog为被观察对象
 */
public class Blog extends Observable {

    private String title;

    public Blog(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void comment(Comment comment) {

        System.out.println(comment.getNickname() + "评论了<" + this.title + "> ," +
                "评论内容:" + comment.getValue());
        // 设置标识位 changed = true,表示被观察者发生了改变
        setChanged();
        // 通知观察者,可以给观察者传递数据
        notifyObservers(comment);
    }

}

Comment类代码:

public class Comment {
    /**
     * 评论者昵称
     */
    private String nickname;
    /**
     * 评论内容
     */
    private String value;

    public Comment(String nickname, String value) {
        this.nickname = nickname;
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public String getNickname() {
        return nickname;
    }
}

Blog类是被观察者对象,被观察者对象需要继承JDK的Observable类,继承后,被观察者对象包含如下属性和方法:

QQ20200511-093515@2x

这些方法都是线程安全方法(加了synchronized同步锁)。

Blog的comment方法中,当博客收到评论时,首先调用父类的setChanged()方法,设置标识位 changed = true,表示被观察者发生了改变;然后调用父类的notifyObservers(Object)方法通知所有观察者。

被观察者对象创建好后,我们接着创建观察者。新建一个Author类:

public class Author implements Observer {

    private String name;

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

    /**
     * 观察者被通知后,就会调用这个方法
     *
     * @param o   被观察者对象
     * @param arg 被观察者传递过来的数据
     */
    @Override
    public void update(Observable o, Object arg) {
        Blog blog = (Blog) o;
        Comment comment = (Comment) arg;
        System.out.println("系统感知到" + this.name + "撰写的博文<" +
                blog.getTitle() + ">收到了" + comment.getNickname() +
                "的评论,评论内容为:" + comment.getValue());
    }
}

观察者对象需要实现JDK的Observer类,重写update方法。当被观察者对象调用了notifyObservers方法后,相应的观察者的update方法会被调用。

新建一个客户端测试一下:

public class Application {

    public static void main(String[] args) {
        Blog blog = new Blog("Java从入门到放弃");
        Author author = new Author("MrBird");

        // 添加观察者
        blog.addObserver(author);

        Comment comment = new Comment("Scott",
                "感谢楼主的文章,让我及时放弃Java,回家继承了千万家产。");
        blog.comment(comment);
    }
}

程序输出如下:

Scott评论了<Java从入门到放弃> ,评论内容:感谢楼主的文章,让我及时放弃Java,回家继承了千万家产。
系统感知到MrBird撰写的博文<Java从入门到放弃>收到了Scott的评论,评论内容为:感谢楼主的文章,让我及时放弃Java,回家继承了千万家产。

值得注意的是,观察者的update方法里的逻辑最好进行异步化,这样在并发环境下可以提升程序性能。

备忘录模式

参考:https://www.cnblogs.com/jimoer/p/9537997.html。

命令模式

暂不记录。

中介者模式

暂不记录。

职责链模式

职责链模式为请求创建一个接收此次请求对象的链。

适用于:

  • 一个请求的处理需要多个对象当中的一个或几个协作处理;

优点:

  1. 请求的发送者和接受者(请求的处理)解耦;
  2. 职责链可以动态的组合。

缺点:

  1. 职责链太长或者处理时间过长,影响性能;
  2. 职责链可能过多。

举个字符串校验的例子。新建一个字符串校验抽象类:

public abstract class StringValidator {

    protected StringValidator validator;

    public void setNextValidator(StringValidator validator) {
        this.validator = validator;
    }

    public abstract void check(String value);
}

StringValidator类包含了一个自身类型的成员变量,这也是该模式的设计核心,以此形成链条。

创建一个校验字符串长度的类StringLengthValidator:

public class StringLengthValidator extends StringValidator {
    @Override
    public void check(String value) {
        if (value != null && value.length() != 0) {
            System.out.println("字符串长度合法");
            if (validator != null) {
                validator.check(value);
            }
        } else {
            System.out.println("字符串长度不合法");
        }
    }
}

上面代码中,在字符串长度校验合法后,我们判断父类的validator属性是否为空,不为空则调用其check方法继续下一步校验。

接着再新建一个校验字符串内容的类StringValueValidator:

public class StringValueValidator extends StringValidator {
    @Override
    public void check(String value) {
        if (value.contains("fuck")) {
            System.out.println("字符串值不合法");
            if (validator != null) {
                validator.check(value);
            }
        } else {
            System.out.println("字符串值合法");
        }
    }
}

套路和StringLengthValidator一样。接着创建一个客户端类,演示下如何让校验类形成一个链条:

public class Application {

    public static void main(String[] args) {
        StringValidator lengthValidator = new StringLengthValidator();
        StringValidator valueValidator = new StringValueValidator();

        lengthValidator.setNextValidator(valueValidator);
        lengthValidator.check("hello");
    }
}

上面代码中,通过StringValidator的setNextValidator方法,我们可以指定下一个校验类,以此形成链条,程序输出如下:

字符串长度合法
字符串值合法

访问者模式

暂不记录🌚

状态模式

暂不记录🌚

posted @   Cassie_Lee  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示