xiaobenchi

导航

设计模式创建型1

理解自大话设计模式

设计模式之工厂模式

1. 简单工厂模式

以设计计算器为例,为了使增加和维护运算方便,可以使用简单工厂模式。为每一种实际的运算设计一个子类,继承同一个父类。根据客户端选择的运算来选择实例化的运算类。

  • 运算类
// 运算父类,在此类中定义参数和子类要重写的方法
public class Operation {
    private double numberA;
    private double numberB;

    public double getNumberA() {
        return numberA;
    }

    public double getNumberB() {
        return numberB;
    }

    public void setNumberA(double numberA) {
        this.numberA = numberA;
    }

    public void setNumberB(double numberB) {
        this.numberB = numberB;
    }

    @Override
    public String toString() {
        return "Operation{" +
                "numberA=" + numberA +
                ", numberB=" + numberB +
                '}';
    }

    public double GetResult(){
       double result = 0;
       return result;
    }
}

/*
* 加法类
* */
class OperationAdd extends Operation{

    @Override
    public double GetResult(){
        double result = 0;
        result = super.getNumberA() + super.getNumberB();
        return result;
    }
}

/*
* 减法类
 */
class OperationSub extends Operation{

    @Override
    public double GetResult(){
        double result = 0;
        result = super.getNumberA() - super.getNumberB();
        return result;
    }
}

/*
* 除法类
 */
class OperationDiv extends Operation{

    @Override
    public double GetResult(){
        double result = 0;
        if(super.getNumberB() == 0){
            System.out.println("除数不能为0!");
        }else{
            result = super.getNumberA() / super.getNumberB();
            return result;
        }
        return result;
    }
}
  • 简单运算工厂类

客户端与此类进行交互,此类负责根据输入来创建不同的运算实例。在后续有添加功能或者修改的功能的时候,只需要修改此类中switch方法的分支。

public class OperationFactory {
    public static Operation createOperate(String operate){
        Operation oper = null;
        switch(operate){
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "/":
                oper  = new OperationDiv();
                break;
        }
        return oper;
    }
}
  • 客户端
public class Client {
    public static void main(String[] args) {
        Operation oper;
        oper = OperationFactory.createOperate("/");
        oper.setNumberA(20);
        oper.setNumberB(10);
        double result = oper.GetResult();
        System.out.println(result);
    }
}

2. 工厂方法模式

简单工厂方法的最大优点在于工厂中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。但是这种方法存在问题,如果你要加一个功能类,我们是一定需要给工厂类的方法里增加‘Case’分支条件的,修改原有的类,这使得我们对扩展开放,对修改也开放了,这就违反了“开放-封闭原则”

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

  • 工厂方法模式结构图

    image-20220716162945888

  • 具体实现类与简单工厂一样

public class Operation {
    private double numberA;
    private double numberB;

    public double getNumberA() {
        return numberA;
    }

    public double getNumberB() {
        return numberB;
    }

    public void setNumberA(double numberA) {
        this.numberA = numberA;
    }

    public void setNumberB(double numberB) {
        this.numberB = numberB;
    }

    @Override
    public String toString() {
        return "Operation{" +
                "numberA=" + numberA +
                ", numberB=" + numberB +
                '}';
    }

    public double GetResult(){
       double result = 0;
       return result;
    }
}

/*
* 加法类
* */
class OperationAdd extends Operation{

    @Override
    public double GetResult(){
        double result = 0;
        result = super.getNumberA() + super.getNumberB();
        return result;
    }
}

/*
* 减法类
 */
class OperationSub extends Operation{

    @Override
    public double GetResult(){
        double result = 0;
        result = super.getNumberA() - super.getNumberB();
        return result;
    }
}

/*
* 除法类
 */
class OperationDiv extends Operation{

    @Override
    public double GetResult(){
        double result = 0;
        if(super.getNumberB() == 0){
            System.out.println("除数不能为0!");
        }else{
            result = super.getNumberA() / super.getNumberB();
            return result;
        }
        return result;
    }
}
  • 增加工厂接口,并为每个运算功能设计工厂类,实现接口
public  interface IFactory {
	
	Operation createOperation();
}

//加法类工厂
class AddFactory implements IFactory{
	
	@Override
	public Operation createOperation() {
		// TODO Auto-generated method stub
		return new OperationAdd();
}
}
//减法类工厂
class SubFactory implements IFactory{
	
	public Operation createOperation() {
		
		return new OperationSub();
	}
}

//除法类工厂
class DivFactory implements IFactory{
	
	public Operation createOperation() {
		
		return new OperationDiv();
	}
}
  • 客户端根据方法名选择实例化工厂
public class Client {

	public  static void main(String[] args) {
		IFactory operFactory = new AddFactory();
		Operation oper = operFactory.createOperation();
		oper.setNumberA(1);
		oper.setNumberB(2);
		double result = oper.GetResult();
		System.out.println(result);
	}
}

3. 抽象工厂模式

​ 在不同的数据库的使用场景中,数据库的查询,插入语句等不尽相同。业务逻辑其实基本相同,所以就需要构建好不同的数据库的类,客户端根据数据库类型进行实例化,进行方法的调用。

  • 抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 抽象工厂方法结构图

抽象工厂方法

  • 抽象工厂模式的优缺点
    • 优点:1. 便于交换产品的系列,具体的工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易。2. 它让具体的创建实例的过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
    • 缺点:如果需要增加项目表,那就要增加多个类,还要修改现有所有的工厂类。

用反射+抽象工厂的数据访问程序

JAVA反射机制就是运行的状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。通过反射机制可以动态的访问类的属性和方法以及字段。

最常用的反射机制就是

Class.forName("obj name").newInstance();
  • 当遇到新的具体工厂角色出现时,如果没有java的反射机制,我们首先需要创建具体的工厂类,在客户端修改相关代码,将具体的工厂的实例化代码加入,严重违反了开闭原则。但是通过java的反射机制就可以将抽象工厂模式实现对修改封闭,对拓展开放,将新的具体的工厂类名写入xml文件中,通过读取xml文件就可以读取到新增的具体工厂的类名,根据类名,字段生成相应类的实例化。

  • 定义:为访问类提供一个创建一组相关或相互依赖的对象接口,且访问类无需指定所要产品的具体类就能得到同族不同等级的产品的模式结构。

  • 应用示例

    • 汽车抽象类&汽车引擎抽象类
    //汽车抽象类
    public abstract class Car{
        protect String brand;
        public void drive(){
            System.out.println("开着"+brand);
        }
    }
    
    //引擎抽象类
    public abstract class Engin{
        String enginName;
        public void showEngin(){
            System.out.println("使用"+enginName+"引擎");
        }
    }
    
    • 几种具体品牌的车,去继承Car
    //奔驰
    public class BenzCar extends Car{
        public BenzCar(){
            brand = "奔驰";
        }
    }
    
    //本田
    public class HondaCar extends Car{
        public HondaCar(){
            brand = "本田";
        }
    }
    
    • 几种具体的车辆发动机,去继承Engine
    //V8发动机
    public class V8Engine extends Engine{
        public V8Engine(){
            super.engineName="V8";
        }
    }
    
    //V12发动机
    public class V12Engine extends Engine{
        public V12Engine(){
            super.engineName="V12";
        }
    }
    
    • 抽象工厂接口
    public interface CarFactory{
        Car produceCar();
        Engine getEngine();
    }
    
    • 具体的车辆生产工厂类,实现CarFactory接口
    //奔驰生产工厂
    public class BenzCarFactory implements CarFactory{
        @override
        public Car produceCar(){
            return new BenzCar();
        }
        
        @override
        public Engine getEngine(){
            return new V12Engin();
        }
    }
    
    //本田生产工厂
    public class HondaCarFactory implements CarFactory{
        @override
        public Car produceCar(){
            return new HondaCar();
        }
        
        @override
        public Engine getEngine(){
            return new V8Engin();
        }
    }
    
    • 客户端代码根据需要选择具体的汽车工厂类来获得汽车对象和发动机对象
    public class Main {
        public static void main(String[] args) throws Exception {
            //选择本田车生产工厂
            CarFactory factory=new HondaCarFactory();
            //当然也可以选择生产奔驰车
            //CarFactory factory=new BenzCarFactory();
            //拿到车
            Car car = factory.produceCar();
            //拿到发动机
            Engine engine = factory.getEngine();
            //展示发动机
            engine.showEngine();
            //开车
            car.drive();
        }
    }
    
  • 使用反射之后,只需要对客户端进行修改

    public class Main {
        public static void main(String[] args) throws Exception {
            //字符串的值可以通过xml来配置
            final String factoryLocation ="com.codefish.factory.HondaCarFactory";
            //选择奔驰车生产工厂(通过反射创建对象)
            CarFactory factory=(CarFactory) Class.forName(factoryLocation).newInstance();
            //拿到车
            Car car = factory.produceCar();
            //拿到发动机
            Engine engine = factory.getEngine();
            engine.showEngine();
            car.drive();
        }
    }
    
    
  • xml文件

<!--工厂集合-->
<factories>
<!--    具体某个客户代码对某个具体工厂的引用-->
    <factory target="com.codefish.Client1" name="com.codefish.factory.HondaCarFactory"/>
    <factory target="com.codefish.Client2" name="com.codefish.factory.BenzCarFactory"/>
    <factory target="com.codefish.ClientXX" name="com.codefish.factory.XXCarFactory"/>
    ...
</factories>

​ 这样如果要替换一个或多个客户代码中的factory类,只要在配置文件里修改即可。不用在每个客户代码文件中修改。而且还可以根据实际需求来配备多个版本的配置文件。

4. 小结

简单工厂模式和工厂模式的使用场景为,实现的不同的功能。简单工厂模式违反了开闭原则,但实现简单,在只需要优化功能的具体实现而不需要扩展的场景下,简单工厂方式是适用的。但当需要拓展时,工厂模式就适用了,每个功能类实现工厂接口,避免了分支结构,符合开闭原则。

抽象工厂适用场景就是同样的功能在不同的场景下由不同的实例去实现。就比如不同的数据库查询和添加的方法语句不同。或者同样是汽车,但是品牌和发动机不同。抽象工厂方法与反射结合,可以符合开闭原则。

posted on 2022-07-19 16:53  小迟在努力  阅读(33)  评论(0)    收藏  举报