【设计模式深度剖析】【2】【创建型】【工厂方法模式】

👈️上一篇:单例模式    |   下一篇:抽象工厂模式👉️

工厂方法模式

工厂方法模式(Factory Method Pattern)又叫虚拟构造函数(Virtual Constructor)模式
或者多态性工厂(PolymorphicFactory)模式
。工厂方法模式的用意是定义一个创建产品对象
的工厂接口,将实际创建性工作推迟到子类中

工厂模式可分为简单工厂、工厂方法和抽象工厂模式。

  • 简单工厂模式中一个工厂类处于对产品类实例化的中心位置上,它知道每一个产品,它决定哪一个产品类应当被实例化

这个模式的优点是允许客户端相对独立于产品创建的过程,并且在系统引入新产品的时候无须修改客户端,即在某种程度上支持“开-闭”原则(一个软件实体应当对扩展开放,对修改关闭)。
这个模式的缺点是对 “开-闭” 原则的支持不够,因为如果有新的产品加入到系统中,则需要修改工厂类,将必要的逻辑加入到工厂类中。

  • 工厂方法模式简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,且克服了它的缺点。

首先,在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做。这个核心类则成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪个产品类应当被实例化的细节。此种进一步抽象化的结果,使这种工厂方法模式可以用来允许系统在不修改具体工厂角色的情况下引进新的产品,这一特点使得工厂模式具有超过简单工厂的优越性。

  • 抽象工厂模式是所有形态的工厂模式中最为抽象和最具有一般性的一种形态

    抽象工厂模式与工厂方法模式的最大区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构

概览

工厂方法模式的定义

英文原话

Define an interface for creating an object, but let subclasses decide which class to instantiate. FactoryMethod lets a class defer instantiation to subclasses.

直译

意思是:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。

工厂方法模式的4个角色

抽象工厂(Creator)角色

该角色是工厂方法模式的核心,与应用系统无关,任何在创建对象的工厂类必须实现这个接口。

具体工厂(Concrete Creator)角色

该角色实现了抽象工厂接口,含有与应用密切相关的逻辑,并且受到应用程序的调用以创建产品对象。

抽象产品(Product)角色

该角色负责定义产品的共性,实现对产品最抽象的定义。

具体产品(Concrete Product)角色

该角色实现抽象产品角色所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品角色的实例。

类图

工厂方法模式类图

代码示例

示例代码

抽象工厂Creator.java
public interface Creator {
  //工厂方法
  //创建一个产品对象,其输入参数类型可自行设置
  public <T extends Product> T factory(Class<T> c);
}
具体工厂ConcreteCreator.java
public class ConcreteCreator implements Creator {

    @Override
    public <T extends Product> T factory(Class<T> c) {
        Product product = null;

        try {
            product= (Product) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
抽象产品Product.java
public interface Product {
    //产品类的公共方法
    public void method1();
    public void method2();
}
具体产品ConcreteProduct.java
public class ConcreteProduct implements Product {

    @Override
    public void method1() {
        //业务逻辑处理代码
        System.out.println("test method1()");
    }

    @Override
    public void method2() {
        //业务逻辑处理代码
    }
}
应用代码ClientFactoryMethodDemo.java
public class ClientFactoryMethodDemo {
    public static void main(String[] args) {
        Creator creator = new ConcreteCreator();
        Product product = creator.factory(ConcreteProduct.class);
        //继续业务处理
        product.method1();
    }
}

[注]:上述通用代码是一个比较实用的、易扩展的框架,可以根据实际项目需要进行扩展应用。

工厂方法模式的应用

工厂方法模式的优点

  1. 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如果一个调用者需要一个具体的产品对象
    只要知道这个产品的类名或约束字符串即可,不用知道创建对象的过程如何,降低了模块间的耦合。
  2. 优秀的可扩展性在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以适应变化
  3. 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,而只需要关心产品的接口,只要接口保持不变,
    系统的上层模块就不需要发生变化
    。因为产品的实例化是由工厂类负责的,具体生产何种产品对象是由不同的工厂类
    决定的。
  4. 工厂方法模式是典型的解耦框架高层模块只需要知道产品的抽象类,其他的实现类都不用关心。
    工厂方法模式符合迪米特法则 (又叫最少知识原则(Least Knowledge Principle,LKP).
    只与你直接的朋友们通信(Only talk to your immediatefriends);
    不要跟“陌生人”说话(Don’t talk to strangers);),也符合依赖倒置原则
    (高层模块不应该依赖低层模块,两者都依赖其抽象;抽象不依赖细节 细节应该依赖于抽象。),只依赖产品类的抽象;
    另外还符合里氏替换原则,可以使用产品子类替换产品父类。

工厂方法模式的使用场景

工厂方法模式在项目中使用得非常频繁,在很多框架的代码中都可以发现工厂方法模式的应用。可使用工厂方法模式的典型场景如下。

  • 工厂方法模式是new一个对象的替代品,因此在所有需要生成对象的地方都可以使用,
    但是需要慎重考虑是否需要增加一个工厂类进行管理,增加代码的复杂度。
  • 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
  • 工厂方法模式可以用在异构项目中,例如,通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围的耦合。
  • 工厂方法模式可以使用在测试驱动开发的框架下。例如,测试一个类 A,就需要将与类A关联的类B也同时产生出来,使用工厂方法模式可以将类B虚拟出来,避免类A与类B的耦合。
  • [注]工厂方法模式还可以与其他模式混合使用(如模板方法模式、单例模式、原型模式等),从而构造出扩展性更好的设计。

工厂方法模式的示例解析

代码示例

通过农场系统演示工厂方法模式的应用(但是工厂方法模式只能针对一个产品等级结构如A产品B产品。但是如果是A产品与B产品的第一等级第二等级,这种情况要抽象工厂模式。)

类图

FuitProductFactory.png

1. 抽象工厂类:FruitGardener.java(果子园接口,生产各类水果的)

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 水果园丁FruitGardener接口是抽象工厂
 *
 * @author Polaris 2024/5/16
 */
public interface FruitGardener {
    Fruit factory();
}

2. 具体工厂类:

2.1 具体的某果园:AppleGardener.java (苹果园类,生产苹果)

该具体工厂-苹果园工厂,封装了实例化具体产品-苹果对象的过程

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 苹果园丁AppleGardener实现FruitGardener接口,是一个用于生产苹果的具体工厂
 *
 * @author Polaris 2024/5/16
 */
public class AppleGardener implements FruitGardener {

    @Override
    public Fruit factory() {
        return new Apple();
    }
}
2.2 具体的某果园:GrapeGardener.java(葡萄园类,生产葡萄)

该具体工厂-葡萄园工厂,封装了实例化具体产品-葡萄对象的过程

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 葡萄园丁GrapeGardener实现FruitGardener接口,是一个用于生产葡萄的具体工厂
 *
 * @author Polaris 2024/5/16
 */
public class GrapeGardener implements FruitGardener {

    @Override
    public Fruit factory() {
        return new Grape();
    }
}

3. 抽象产品类:Fruit.java(水果接口,抽象的水果类)

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 水果Fruit接口是抽象产品
 *
 * @author Polaris 2024/5/16
 */
public interface Fruit {
    //生长
    void grow();

    //收获
    void harvest();

    //栽种
    void plant();
}

4. 具体产品类:

4.1 具体的某类水果:Apple.java(苹果类,是一种具体的水果)

由于苹果是一类具体的水果类,因此它对抽象的水果类进行了实现,当然同时它也有独自属于它自己的方法。

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 苹果Apple实现Fruit接口,是一个具体产品
 *
 * @author Polaris 2024/5/16
 */
public class Apple implements Fruit {

    private int treeAge;

    public int getTreeAge() {
        return treeAge;
    }

    public void setTreeAge(int treeAge) {
        this.treeAge = treeAge;
    }

    @Override
    public void grow() {
        System.out.println("苹果树在生长");
    }

    @Override
    public void harvest() {
        System.out.println("收获苹果");
    }

    @Override
    public void plant() {
        System.out.println("栽种苹果");
    }
}
4.2 具体的某类水果:Grape.java(葡萄类,是一种具体的水果)

由于葡萄是一类具体的水果类,因此它对抽象的水果类进行了实现,当然同时它也有独自属于它自己的方法。

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 葡萄Grape实现Fruit接口,是一个具体产品
 *
 * @author Polaris 2024/5/16
 */
public class Grape implements Fruit {

    private boolean seedless;
    public boolean isSeedless(){
        return seedless;
    }
    public void setSeedless(boolean seedless){
        this.seedless=seedless;
    }
    @Override
    public void grow() {
        System.out.println("葡萄正在生长...");
    }

    @Override
    public void harvest() {
        System.out.println("收获葡萄");
    }

    @Override
    public void plant() {
        System.out.println("栽种葡萄");
    }
}

5. 测试类

具体某类水果工厂来生产对应的某水果对象,

该水果对象是某具体水果类的实例,

而该具体水果实例实现了抽象的水果类,

里氏替换原则,父类的引用可以指向子类实例,因为子类类型一定是父类类型(或者实现的接口的类型)

package com.polaris.designpattern.list1.creational.pattern2.factorymethod;

/**
 * 下面是一个应用场景代码
 *
 * @author Polaris 2024/5/16
 */
public class ClientDemo {
    public static void main(String[] args) {
        //苹果园丁工厂
        FruitGardener fruitGardener = new AppleGardener();
        //通过工厂生产苹果
        Fruit apple = fruitGardener.factory();
        apple.plant();
        apple.grow();
        apple.harvest();
        fruitGardener = new GrapeGardener();
        //通过工厂生产葡萄
        Fruit grape = fruitGardener.factory();
        grape.plant();
        grape.grow();
        grape.harvest();
    }
}

👈️上一篇:单例模式    |   下一篇:抽象工厂模式👉️

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值