从问题角度来思考设计模式(1) – 生成编

目录

  1. 生成编
  2. 结构编
  3. 行为编

简介

关于设计模式可以理解为,那些经常会遇到的问题以及如何解决所洗练出来的总结。所以我们在考虑进行设计时,首先要做的就是查询当前所面临的问题,是否已经有很好的解决方案,而不是闭门造车。身边有些同事喜欢需求来了,直接上来就撸代码,认为为了迎合设计模式而设计的工作,会有过度设计的嫌疑。什么时候是导入新工具,新技术的最佳时机?当一个事物让人难以忍受的时候,这时改变所带来的团队收益远大于痛苦程度,就可以很平缓地推进。反之,强推工具反而引来的不是技术上的问题,而是人为的抵制。是不是很奇妙!

本文主要通过问题配合设计模式,来说明设计模式所带来的前后效果,加深大家各种模式的印象。

如何让构造器的用途更容易理解

  • 改造前
public class FooClient {

    private final String url;
    private final String proxyHost;
    private final int proxyPort;

    public FooClient(String url) {
        this.url = url;
        this.proxyHost = null;
        this.proxyPort = -1;
    }

    public FooClient(String url, String proxyHost, int proxyPort) {
        this.url = url;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
    }
}
new FooClient(url);
new FooClient(url, proxyHost, proxyPort);

通过参数个数来控制生成的实例是否具备代理功能

  • 改造后
    Factory Method
public class FooClient {

    private final String url;
    private final String proxyHost;
    private final int proxyPort;

    private FooClient(String url, String proxyHost, int proxyPort) {
        this.url = url;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
    }

    public static FooClient withoutProxy(String url) {
        return new FooClient(url, null, -1);
    }

    public static FooClient withProxy(String url, String proxyHost, int proxyPort) {
        return new FooClient(url, proxyHost, proxyPort);
    }
}
FooClient.withoutProxy(url);
FooClient.withProxy(url, proxyHost, proxyPort);

通过Factory Method封装让外部调用更友好

根据条件参数创建子类实例时,如何隐藏底层细节

  • 改造前
public class FooDateFilter extends FooFilter {}
public class FooNumberFilter extends FooFilter {}
public class FooStringFilter extends FooFilter {}
FooFilter filter = new FooStringFilter("foo");

当条件参数为字符串时,调用方需要知道要用FooStringFilter子类
当条件参数为数字时,调用方需要知道要用FooNumberFilter子类

  • 改造后
    Factory Method
public class FooDateFilter extends FooFilter {}
public class FooNumberFilter extends FooFilter {}
public class FooStringFilter extends FooFilter {}

public class FooFilterFactory {

    private FooFilterFactory() {}

    public static FooFilter create(Date date) {
        return new FooDateFilter(date);
    }

    public static FooFilter create(Number number) {
        return new FooNumberFilter(number);
    }

    public static FooFilter create(String string) {
        return new FooStringFilter(string);
    }
}
FooFilter filter = FooFilterFactory.create("foo");

通过Factory Method将子类生成实例细节隐藏,调用方不用再关心需要调用哪个子类。还有,将来如果要将某个子类(如:FooStringFilter)废弃,升级到FooStringFilter V2版本,也只需要更改FooFilterFactory文件即可。对调用方来说还是调用FooFilterFactory.create来生成实例,从而避免因为修改某个子类,造成调用方的代码改动。

想通过1个接1个字段来配置生成实例

  • 改造前
public class FooWindow {

    private final int width;
    private final int height;
    private final FooColor color;
    private final FooColor backgroundColor;

    public FooWindow() {
        this.width = 0;
        this.height = 0;
        this.color = null;
        this.backgroundColor = null;
    }

    public FooWindow(int width, int height, FooColor color, FooColor backgroundColor) {
        this.width = width;
        this.height = height;
        this.color = color;
        this.backgroundColor = backgroundColor;
    }

    public FooWindow withWidth(int width) {
        return new FooWindow(width, this.height, this.color, this.backgroundColor);
    }

    public FooWindow withHeight(int height) {
        return new FooWindow(this.width, height, this.color, this.backgroundColor);
    }

    //color, backgroundColor同上
}
new FooWindow()
  .withWidth(100)
  .withHeight(200)
  .withColor(color)
  .withBackgroundColor(backgroundColor);

每调用一个链式方法都会生成一个新实例替代老的实例,开销较大。

  • 改造后
    Builder
public class FooWindow {

    private final int width;
    private final int height;
    private final FooColor color;
    private final FooColor backgroundColor;

    public FooWindow(int width, int height, FooColor color, FooColor backgroundColor) {
        this.width = width;
        this.height = height;
        this.color = color;
        this.backgroundColor = backgroundColor;
    }

    public static class FooWindowBuilder {

        private int width;
        private int height;
        private FooColor color;
        private FooColor backgroundColor;

        public FooWindowBuilder withWidth(int width) {
            this.width = width;
            return this;
        }

        public FooWindowBuilder withHeight(int height) {
            this.height = height;
            return this;
        }

        //color, backgroundColor同上

        public FooWindow build() {
            return new FooWindow(this.width, this.height, this.color, this.backgroundColor);
        }
    }
}
new FooWindowBuilder()
  .withWidth(100)
  .withHeight(200)
  .withColor(color)
  .withBackgroundColor(backgroundColor)
  .build();

在给实例配置成员值时,并不像改造前每回都生成一个新的实例。通过Builder模式,改变builder实例的值来控制最终生成FooWindow实例。
在本例中,FooWindow只有4个参数,当对象的参数变得更多时,改造前每个属于设置方法都需要修改且考虑各参数的排序问题。而改造后只关注单一的属性,很容易扩展。

参考链接

https://qiita.com/akira_/items/8fa2522dc25e98c2d6e4

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注