《设计模式》工厂方法模式

发布于:2025-08-18 ⋅ 阅读:(20) ⋅ 点赞:(0)

1.工厂方法模式(Factory Method)定义

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

1.1 UML图:

主要有4个对象:

  • 抽象工厂(Abstract Creator):提供一个创建产品的接口。调用者可以通过它访问具体工厂的工厂方法。
  • 具体工厂(Concrete Creator):继承自抽象工厂,并实现其创建对象的方法。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(Concrete Product):实现了抽象产品中所定义的接口,由具体工厂来创建,与同具体工厂之间是一一对应的。
    在这里插入图片描述

2.工厂方法模式举例:

业务场景:需要实现一个商场收银系统,有四种策略,

  1. 正常结账
  2. 打折
  3. 满减
  4. 先打折再满减
    简单工厂模式 -> 工厂方法模式:

简单工厂模式UML图:简单工厂 + 策略模式 + 装饰模式 具体实现逻辑,见装饰模式 -> 2.装饰模式举例:
在这里插入图片描述
工厂方法模式UML:策略模式 + 装饰模式 + 工厂方法模式
在这里插入图片描述

2.2 核心代码:

ISale接口

public interface ISale {

   public double acceptCash(double price,int num);

}

IFactory接口

public interface IFactory {

    public ISale createSalesModel(); //创建销售模式

}

CashContex:

public class CashContext {
    private ISale cs;   //声明一个ISale接口对象
    //通过构造方法,传入具体的收费策略
    public CashContext(int cashType){
        IFactory fs=null;
        switch(cashType) {
            case 1://原价
                fs = new CashRebateReturnFactory(1d,0d,0d);
                break;
            case 2://8折
                fs = new CashRebateReturnFactory(0.8d,0d,0d);
                break;
            case 3://7折
                fs = new CashRebateReturnFactory(0.7d,0d,0d);
                break;
            case 4://300100
                fs = new CashRebateReturnFactory(1,300d,100d);
                break;
            case 5://先打8,再满300100
                fs = new CashRebateReturnFactory(0.8d,300d,100d);
                break;
            case 6://先满20050,再打7折
                fs = new CashReturnRebateFactory(0.7d,200d,50d);
                break;
        }
        this.cs = fs.createSalesModel();
    }

    public double getResult(double price,int num){
        //根据收费策略的不同,获得计算结果
        return this.cs.acceptCash(price,num);
    }
}

CashRebateReturnFactory

public class CashRebateReturnFactory implements IFactory {

    private double moneyRebate = 1d;
    private double moneyCondition = 0d;
    private double moneyReturn = 0d;

    public CashRebateReturnFactory(double moneyRebate,double moneyCondition,double moneyReturn){
        this.moneyRebate=moneyRebate;
        this.moneyCondition=moneyCondition;
        this.moneyReturn=moneyReturn;
    }

    //先打x折,再满m返n
    public ISale createSalesModel(){

        CashNormal cn = new CashNormal();
        CashReturn cr1 = new CashReturn(this.moneyCondition,this.moneyReturn);
        CashRebate cr2 = new CashRebate(this.moneyRebate);

        cr1.decorate(cn);   //用满m返n算法包装基本的原价算法
        cr2.decorate(cr1);  //打x折算法装饰满m返n算法
        return cr2;         //将包装好的算法组合返回
    }
}

CashReturnRebateFactory

public class CashReturnRebateFactory implements IFactory {

    private double moneyRebate = 1d;
    private double moneyCondition = 0d;
    private double moneyReturn = 0d;

    public CashReturnRebateFactory(double moneyRebate,double moneyCondition,double moneyReturn){
        this.moneyRebate=moneyRebate;
        this.moneyCondition=moneyCondition;
        this.moneyReturn=moneyReturn;
    }

    //先满m返n,再打x折
    public ISale createSalesModel(){

        CashNormal cn2 = new CashNormal();
        CashRebate cr3 = new CashRebate(this.moneyRebate);
        CashReturn cr4 = new CashReturn(this.moneyCondition,this.moneyReturn);

        cr3.decorate(cn2);  //用打x折算法包装基本的原价算法
        cr4.decorate(cr3);  //满m返n算法装饰打x折算法
        return cr4;         //将包装好的算法组合返回
    }
}

CashSuper

public class CashSuper implements ISale {

    protected ISale component;

    //装饰对象
    public void decorate(ISale component) {
        this.component=component;
    }

    public double acceptCash(double price,int num){
        double result = 0d;
        if (this.component != null){
            //若装饰对象存在,则执行装饰的算法运算
            result = this.component.acceptCash(price,num);    
        }
        return result;
    }
}

CashReturn

public class CashReturn extends CashSuper {

    private double moneyCondition = 0d; //返利条件
    private double moneyReturn = 0d;    //返利值

    //返利收费。初始化时需要输入返利条件和返利值。
    //比如“满300返100”,就是moneyCondition=300,moneyReturn=100
    public CashReturn(double moneyCondition,double moneyReturn){
        this.moneyCondition = moneyCondition;
        this.moneyReturn = moneyReturn;
    }

    //计算收费时,当达到返利条件,就原价减去返利值
    public double acceptCash(double price,int num){
        double result = price * num;
        if (moneyCondition>0 && result >= moneyCondition)
            result = result - Math.floor(result / moneyCondition) * moneyReturn; 
        return super.acceptCash(result,1);   
    }
    
}

CashRebate

public class CashRebate extends CashSuper {

    private double moneyRebate = 1d;
    //打折收费。初始化时必需输入折扣率。八折就输入0.8
    public CashRebate(double moneyRebate){
        this.moneyRebate = moneyRebate;
    }

    //计算收费时需要在原价基础上乘以折扣率
    public double acceptCash(double price,int num){
        double result = price * num * this.moneyRebate;
        return super.acceptCash(result,1);
    }
    
}

CashNormal

public class CashNormal implements ISale {
    //正常收费,原价返回
    public double acceptCash(double price,int num){
        return price * num; 
    }    
}

DemoTest

public class Demotest {

	public static void main(String[] args){

		System.out.println("**********************************************");
		System.out.println("工厂方法模式");
		System.out.println();

		int discount = 0; 		//商品折扣模式
		double price = 0d; 		//商品单价
		int num = 0;			//商品购买数量
		double totalPrices = 0d;//当前商品合计费用
		double total = 0d;		//总计所有商品费用

		Scanner sc = new Scanner(System.in);

		do {
			System.out.println("商品折扣模式如下:");
			System.out.println("1.正常收费");
			System.out.println("2.打八折");
			System.out.println("3.打七折");
			System.out.println("4.满300送100");
			System.out.println("5.先打8折,再满300送100");
			System.out.println("6.先满200送50,再打7折");
			System.out.println("请输入商品折扣模式:");
			discount = Integer.parseInt(sc.nextLine());
			System.out.println("请输入商品单价:");
			price = Double.parseDouble(sc.nextLine());
			System.out.println("请输入商品数量:");
			num = Integer.parseInt(sc.nextLine());
			System.out.println();

			if (price>0 && num>0){

				//根据用户输入,将对应的策略对象作为参数传入CashContext对象中
				CashContext cc = new CashContext(discount);

				//通过Context的getResult方法的调用,可以得到收取费用的结果
				//让具体算法与客户进行了隔离
				totalPrices = cc.getResult(price,num);

				total = total + totalPrices;

				System.out.println();
				System.out.println("单价:"+ price + "元 数量:"+ num +" 合计:"+ totalPrices +"元");
				System.out.println();
				System.out.println("总计:"+ total+"元");
				System.out.println();
			}
		}
		while(price>0 && num>0);

		System.out.println();
		System.out.println("**********************************************");

	}
}

输出结果:
在这里插入图片描述

3. 工厂方法模式的优缺点;

  • 优点:
    • 可以避免创建者和具体产品之间的紧密耦合,针对工厂接口编程,而并非具体实现类。
    • 单一职责原则。可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
    • 开闭原则。无需更改现有客户端代码,就可以在程序中引入新的产品类型。
  • 缺点:
    • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度。

4. 总结

  • 通常我们把被创建的对象称之为【产品】, 创建【产品】的对象称为【工厂】。
  • 当产品比较固定且数量少的情况下,只需要一个工厂类就可以,称之为【简单工厂】, 多个工厂时,就称为工厂方法模式,其中工厂方法使一个类的实例化延迟到其子类,而简单工厂实例化就是在唯一工厂类

5.参考

  • https://www.cnblogs.com/mklblog/p/18029716

网站公告

今日签到

点亮在社区的每一天
去签到