工厂方法模式:概念与应用

发布于:2024-07-05 ⋅ 阅读:(19) ⋅ 点赞:(0)

工厂方法模式

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

工厂方法模式结构

在这里插入图片描述

  1. 产品 (Product) 将会对接口进行声明。 对于所有由创建者及其子类构建的对象, 这些接口都是通用的。

  2. 具体产品 (Concrete Products) 是产品接口的不同实现。

  3. 创建者 (Creator) 类声明返回产品对象的工厂方法。 该方法的返回对象类型必须与产品接口相匹配。

    注意, 尽管它的名字是创建者, 但它最主要的职责并不是创建产品。 一般来说, 创建者类包含一些与产品相关的核心业务逻辑。 工厂方法将这些逻辑处理从具体产品类中分离出来。

  4. 具体创建者 (Concrete Creators) 将会重写基础工厂方法, 使其返回不同类型的产品。

    注意, 并不一定每次调用工厂方法都会创建新的实例。 工厂方法也可以返回缓存、 对象池或其他来源的已有对象。

通用代码结构:

//创建者类,
public abstract class Creator{
	public abstract <T extends Product>T CreatProduct(Class<T> c);
}

//具体创建者类
public class ConcreteCreator extends Creator{
	public abstract T<T extends Product> CreatProduct(Class<T> c){
	Product product=null;
	try{
		product =(Product)Class.forName(c.getName()).newInstance();
	}catch(Exception e){
	....
	}
	return (T)product;
	}
}

//产品类接口
public interface Product{
	//产品的一些公共特性
}

// 具体产品类,提供产品接口的各种实现。
public class ConcreteProduct implements Product{

}

工厂方法适合的应用场景

1.无法预知对象确切类别及其依赖关系时, 可使用工厂方法。

工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。

例如, 如果需要向应用中添加一种新产品, 你只需要开发新的创建者子类, 然后重写其工厂方法即可。

2.如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。

继承可能是扩展软件库或框架默认行为的最简单方法。 但是当你使用子类替代标准组件时, 框架如何辨识出该子类?

解决方案是将各框架中构造组件的代码集中到单个工厂方法中, 并在继承该组件之外允许任何人对该方法进行重写。

让我们看看具体是如何实现的。 假设你使用开源 UI 框架编写自己的应用。 你希望在应用中使用圆形按钮, 但是原框架仅支持矩形按钮。 你可以使用 圆形按钮Round­Button子类来继承标准的 按钮Button类。 但是, 你需要告诉 UI框架UIFramework类使用新的子类按钮代替默认按钮。为了实现这个功能, 你可以根据基础框架类开发子类 圆形按钮 UIUIWith­Round­Buttons , 并且重写其 create­Button创建按钮方法。 基类中的该方法返回 按钮对象, 而你开发的子类返回 圆形按钮对象。 现在, 你就可以使用 圆形按钮 UI类代替 UI框架类。 就是这么简单!

在这里插入图片描述

工厂方法模式的优缺点

工厂方法模式的优点:

  • 避免创建者与具体的产品逻辑耦合

  • 满⾜单⼀职责,每⼀个业务逻辑实现都在所属⾃⼰的类中完成

  • 满⾜开闭原则,⽆需更改使⽤调⽤⽅就可以在程序中引⼊新的产品类型

工厂方法模式的缺点:

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

抽象工厂模式和声明为 abstract的简单工厂不是同一个,不要弄混了。抽象工厂更多的是产生一系列对象。

练手题目

题目描述

小明家有两个工厂,一个用于生产圆形积木,一个用于生产方形积木,请你帮他设计一个积木工厂系统,记录积木生产的信息。

输入描述

输入的第一行是一个整数 N(1 ≤ N ≤ 100),表示生产的次数。接下来的 N 行,每行输入一个字符串和一个整数,字符串表示积木的类型。积木类型分为 “Circle” 和 “Square” 两种。整数表示该积木生产的数量

输出描述

对于每个积木,输出一行字符串表示该积木的信息。

在这里插入图片描述

提示信息

在示例中,积木工厂生产了3块积木,其中有2块是圆形积木,1块是方形积木。根据输入的类型,每块积木的信息被输出到控制台。

解题:

1、简单工厂模式代码实现:

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

// 抽象积木接口
interface Block {
    void produce();
}

// 具体圆形积木实现
class CircleBlock implements Block {
    @Override
    public void produce() {
        System.out.println("Circle Block");
    }
}

// 具体方形积木实现
class SquareBlock implements Block {
    @Override
    public void produce() {
        System.out.println("Square Block");
    }
}

// 抽象积木工厂接口
interface BlockFactory {
    Block createBlock();
}

// 具体圆形积木工厂实现
class CircleBlockFactory implements BlockFactory {
    @Override
    public Block createBlock() {
        return new CircleBlock();
    }
}

// 具体方形积木工厂实现
class SquareBlockFactory implements BlockFactory {
    @Override
    public Block createBlock() {
        return new SquareBlock();
    }
}

// 积木工厂系统
class BlockFactorySystem {
    private List<Block> blocks = new ArrayList<>();

    public void produceBlocks(BlockFactory factory, int quantity) {
        for (int i = 0; i < quantity; i++) {
            Block block = factory.createBlock();
            blocks.add(block);
            block.produce();
        }
    }

    public List<Block> getBlocks() {
        return blocks;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 创建积木工厂系统
        BlockFactorySystem factorySystem = new BlockFactorySystem();

        // 读取生产次数
        int productionCount = scanner.nextInt();
        scanner.nextLine();

        // 读取每次生产的积木类型和数量
        for (int i = 0; i < productionCount; i++) {
            String[] productionInfo = scanner.nextLine().split(" ");
            String blockType = productionInfo[0];
            int quantity = Integer.parseInt(productionInfo[1]);

            if (blockType.equals("Circle")) {
                factorySystem.produceBlocks(new CircleBlockFactory(), quantity);
            } else if (blockType.equals("Square")) {
                factorySystem.produceBlocks(new SquareBlockFactory(), quantity);
            }
        }
    }
}

2.利用反射实现工厂方法

import java.lang.reflect.Constructor;
import java.util.Scanner;

// 抽象工厂类
abstract class BlocksFactory {
    public abstract blocks createBlocks(Class<blocks> c, String str, int num);
}

//具体工厂实现类 
class BlocksFactoryImpl extends BlocksFactory {
		
    @Override
    public blocks createBlocks(Class<blocks> c, String str, int num) {
        try {
			       //反射获取blocks类中带有两个参数的构造器
            Constructor<blocks> constructor = c.getConstructor(String.class, int.class);
            return constructor.newInstance(str, num);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

//定义积木接口
interface blocks {
    void blockPrint(String str, int num);
}

//圆形积木 
class CircleBlocks implements blocks{
    private String str;
    private int num;
     
    public CircleBlocks(String str, int num) {
        this.str = str;
        this.num = num;
    }
     
    public void blockPrint(String str,int num){
        for(int i=0;i<num;i++){
            System.out.println(str+" Block");
        }
    }
}

//方形积木 
class SquareBlocks implements blocks{
     
    private String str;
    private int num;
     
    public SquareBlocks(String str, int num) {
        this.str = str;
        this.num = num;
    }
     
    public void blockPrint(String str,int num){
        for(int i=0;i<num;i++){
            System.out.println(str+" Block");
        }
    }
}
 
 
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        
        //获取工厂实例
        BlocksFactory factory = new BlocksFactoryImpl();
 
        for (int n = 0; n < N; n++) { 
            String type = scanner.next();//用户输入类型(Circle 或 Square)
            int num = scanner.nextInt();// 用户输入数量
            
            //构建正确的类名,并第一个字母大写
            String className = type.substring(0, 1).toUpperCase() + type.substring(1) + "Blocks"; // 构造正确的类名
            try {
		            //使用工厂方法创建对象
                blocks block = factory.createBlocks((Class<blocks>) Class.forName(className), type, num);
                if (block != null) {
                    block.blockPrint(type, num);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
 
        scanner.close();
    }
}