工厂方法模式

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型

定义Button接口
public interface Button {
    void render();
    void onClick(Runnable f);

}
WindowButton实现类
public class WindowsButton implements Button{

    @Override
    public void render() {
        // 根据 WINDOW 样式渲染按钮
    }

    @Override
    public void onClick(Runnable f) {
        // 绑定本地操作系统点击事件
    }
}
HTMLButton实现类
public class HTMLButton implements Button{

    @Override
    public void render() {
        // 根据 HTMl 样式渲染按钮
    }

    @Override
    public void onClick(Runnable f) {
        // 绑定网络浏览器点击事件
    }
}
声明工厂抽象类
// 创建者类声明的工厂方法必须返回一个产品类的对象。创建者的子类通常会提供该方法的实现。
abstract class Dialog {
    // 创建者还可提供一些工厂方法的默认实现。
    abstract Button createButton();

    // 请注意,创建者的主要职责并非是创建产品。其中通常会包含一些核心业务
    // 逻辑,这些逻辑依赖于由工厂方法返回的产品对象。子类可通过重写工厂方
    // 法并使其返回不同类型的产品来间接修改业务逻辑。
    void render() {
        // 调用工厂方法创建一个产品对象。
        Button okButton = createButton();
        // 现在使用产品。
        okButton.onClick(this::closeDialog);
        okButton.render();
    }
    private void closeDialog() {

    }
}
WebDialog实现抽象工厂
public class WebDialog extends Dialog{
    @Override
    Button createButton() {
        return new HTMLButton();
    }
}
WindowDialog实现抽象工厂
public class WindowsDialog extends Dialog{
    @Override
    Button createButton() {
        return new WindowsButton();
    }
}
public class Application {
    private Dialog dialog;

    // 程序根据当前配置或环境设定选择创建者的类型。
    void initialize() {
        // 假设readApplicationConfigFile()返回Config类型的对象,Config类包含OS属性。
        Config config = readApplicationConfigFile();
        if (config.getOS().equals("Windows")) {
            dialog = new WindowsDialog();
        } else if (config.getOS().equals("Web")) {
            dialog = new WebDialog();
        } else {
            System.out.println(config.getOS());
        }
    }
    // 当前客户端代码会与具体创建者的实例进行交互,但是必须通过其基本接口
    // 进行。只要客户端通过基本接口与创建者进行交互,你就可将任何创建者子
    // 类传递给客户端。
    void main() {
        this.initialize();
        dialog.render();
    }

    // 假设的Config类
    private static class Config {
        private String OS;

        public String getOS() {
            return OS;
        }

        public Config() {
            this.OS = System.getProperty("os.name");
        }
    }
    // 假设的读取配置文件方法
    private Config readApplicationConfigFile() {
        // 读取配置文件并返回Config对象的逻辑
        return new Config();
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.main();
    }
}

实现方式

  1. 让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。

  2. 在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。

  3. 在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用, 同时将创建产品的代码移入工厂方法

  4. 如果应用中的产品类型太多, 那么为每个产品创建子类并无太大必要, 这时你也可以在子类中复用基类中的控制参数

  5. 如果代码经过上述移动后, 基础工厂方法中已经没有任何代码, 你可以将其转变为抽象类。 如果基础工厂方法中还有其他语句, 你可以将其设置为该方法的默认行为

工厂模式方法优缺点:

优点:你可以避免创建者和具体产品之间的紧密耦合

单一职责原则。 你可以将产品创建代码放在程序的单一位置, 从而使得代码更容易维护

开闭原则。 无需更改现有客户端代码, 你就可以在程序中引入新的产品类型

缺点:应用工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中