JAVA设计模式之简单工厂、工厂方法

工厂模式是设计模式中最最常用的一种,属于对象创建型设计模式,简单说就是一种不使用new关键字创建对象的设计模式,其用意为解耦,降低代码重构难度。
工厂模式分为简单工厂、工厂方法。


一、简单工厂

简单工厂模式的起源很简单,假设一种场景:对象X中调用对象Y和Z,Y和Z同时继承了M对象。最简单的做法为在X中分别new出Y和Z的新对象,这种方法分别调用了Y和Z的构造方法,使用起来很方便,但以后如果想修改Y为Z,则修改的难度就会与调用Y的次数成正比。那么有没有一种方法,能在一个地方修改,即可将new Y全部修改成new Z呢?
这就是工厂模式的起源。简单工厂的做法很直观,就是使用一个工厂类来创建Y和Z对象。通过传入特定字段的判断,来创建不同的对象,在使用时,外部使用共同父类M接收返回的对象即可。
简单工厂模式有三个角色:
工厂类:负责根据特定的标志生成不同的对象实例;
共同父类:拥有子类共同属性,用于工厂模式调用时接收实例;
具体子类:实现各个子类特定属性
举一个例子:有一个CarFactory负责生产不同品牌的汽车,根据客户传入汽车品牌,生产不同品牌汽车实例。汽车对象有bwm和audi两种,拥有共同父类Car。实例代码如下:

//接口父类
public interface Car {
    void printBrand();
}

//bwm子类
public class Bmw implements Car {

    public void printBrand() {
        System.out.println("this is a bmw Car");
    }
}

//audi子类
public class Audi implements Car {
    public void printBrand() {
        System.out.println("this is an audi Car");
    }
}

//工厂方法
public class CarFactory {

    public static Car produceCar(String brand){
        if ("bwm".equalsIgnoreCase(brand)){
            return new Bmw();
        }else if ("audi".equalsIgnoreCase(brand)){
            return new Audi();
        }else {
            return null;
        }
    }

}

调用方法如下:

public static void main(String[] args){
    Car car1 = CarFactory.produceCar("audi");
    Car car2 = CarFactory.produceCar("bwm");
    car1.printBrand();
    car2.printBrand();
 }

运行 结果如下:

this is an audi Car
this is a bmw Car

以上实例可以看出,工厂类并没有任何业务实现,只是提供了生产对象的方式。如果要将对应的汽车修改为另外的,我们只要修改传入工厂的方法即可。但如果我的汽车品牌有1000个呢?要使用1000个if…else if也太扯淡了。那么我们这种简单工厂模式是不是不实用呢?不是的,进行一些优化就可以很方便地使用了。对这种形式的优化首先会想到泛型,但比泛型的更合适的是反射。
使用反射方式首先要修改工厂方法:

public class CarFactory {

    public static Car produceCar(String carClassName) throws Exception{
        Class clazz = Class.forName(carClassName);
        return (Car)clazz.newInstance();
    }

}

调用方式为:

public static void main(String[] args) throws Exception{
    Car car1 = CarFactory.produceCar("com.test.Audi");
    Car car2 = CarFactory.produceCar("com.test.Bwm");
    car1.printBrand();
    car2.printBrand();
}

运行结果与上面一样。有同学可能发现了,使用反射需要全限定类名,那么写那么多全限定类名是不是太累了?对的,对这种全限定类名,可以使用一个常量类将其设置为常量,或是使用配置文件,这样是不是很方便?
当然,使用反射的方式只是其中一种方法,更好的方式请看下节,工厂方法。

二、工厂方法

前文所说,简单工厂存在一系列的缺陷,总结下大体可以分为以下三种:

  1. 简单 工厂的工厂类中包含所有实例的创建逻辑,一旦该工厂类出问题,所有的实例都无法创建;
  2. 违背设计模式的开闭原则:在添加新实例的时候,必须要修改工厂类中的原有逻辑,久而久之会使工厂类更加复杂(前文中的反射方式解决了这个问题,但也会造成配置文件的负担等问题);
  3. 工厂类中使用了static修饰方法,无法被继承和重写,造成问题。

工厂方法模式与简单工厂模式不同,通过定义父类工厂定义创建对象的接口,生成具体对象则由子类负责。其实就是将简单工厂的方法抽象为接口,通过多态即可实现多种子类不同实现。所以工厂方法也称为多态工厂。
工厂方法模式需要以下四种角色:

  1. 抽象工厂:具体工厂的父类,定义工厂方法的接口;
  2. 具体工厂:抽象工厂的子类,定义了实例创建的具体实现,被外界调用;
  3. 抽象产品:具体产品的父类,定义产品的公共接口;
  4. 具体产品:描述生产的具体产品。

工厂方法的类示意图如下:
JAVA设计模式之简单工厂、工厂方法_第1张图片

接下来用一个例子说明,还是用之前生产汽车的例子,我们在品牌之上再加一个车辆类别,假设有轿车和跑车两种。首先将之前简单工厂的汽车类抽象出来:

public interface Car {
    void display();
}

然后具体产品和类别的汽车实现抽象产品来实现:

public class BMWCar implements Car{

    public void display() {
        System.out.println("This is a car of bmw!");
    }
}
public class BMWSuperCar implements Car{

    public void display() {
        System.out.println("This is a super car of bmw!");
    }
}
public class AudiCar implements Car{

    public void display() {
        System.out.println("This is a car of audi!");
    }
}
public class AudiSuperCar implements Car{

    public void display() {
        System.out.println("This is a super car of audi!");
    }
}

实现了抽象产品和具体产品之后,定义公共的抽象工厂角色,如下:

public interface AbstartCarFactory {
    Car product(String classic);
}

具体工厂实现如下:

public class BMWFactory implements AbstartCarFactory{

    public Car product(String classic){
        if ("superCar".equalsIgnoreCase(classic)){
            return new BMWSuperCar();
        }else if ("car".equalsIgnoreCase(classic)){
            return new BMWCar();
        }
        return null;
    }
}
public class AudiFactory implements AbstartCarFactory{

    public Car product(String classic){
        if ("superCar".equalsIgnoreCase(classic)){
            return new AudiSuperCar();
        }else if ("car".equalsIgnoreCase(classic)){
            return new AudiCar();
        }
        return null;
    }
}

至此,工厂方法的四个角色全部定义完毕,接下来就是调用了。比如要生产一辆宝马的跑车和一辆奥迪的轿车,调用可以如下:

public class CarTest {

    public static void main(String[] args) throws Exception{
        //宝马车
        AbstartCarFactory bwmFactory = new BMWFactory();
        Car car1 = bwmFactory.product("superCar");
        car1.display();

        AbstartCarFactory audiFactory = new AudiFactory();
        Car car2 = bwmFactory.product("car");
        car2.display();
    }
}

得到输出如下:

This is a super car of bmw!
This is a car of bmw!

可以看出,工厂方法模式比简单工厂结构更加清晰,如果要增加新的产品,工厂方法更加方便。其实类别也可以抽象出来,这里就不一一演示了。抽象工厂和抽象产品,不一定是接口,也可以是抽象类或者普通类。java8中增加了default使得接口方法不一定每个子类都要实现,所以选择接口会更加清晰。

好了,以上就是工厂模式的全部内容了,希望大家喜欢。

你可能感兴趣的:(设计模式)