工厂办法形式

前语

今天想聊一下陈词滥调的一种形式,工厂办法形式。别的本文中会呈现很多的代码,不过都很简略容易理解。

下面两张图,是本文中可能会用到的。感兴趣的能够看一下。期望我们能够看完本文,喜欢的话能够点个赞支持下。

常见的规划准则

规划准则.png

工厂形式的种类

工厂形式.png

讲个故事

这几年新能源车比较盛行。特斯拉,比亚迪,五菱等车企都在发展新能源车。轿车嘛,品牌不同,配置不同,价格也不太相同。毕业生小白就职于一家轿车媒体公司,一天组长把他叫来,让他搜集一下这些车企新能源车的相关信息。今天的故事就从这儿开始了。

正常的写法

收到使命后的小白心想,这个使命太简略了,没有任何难度。所以他立刻开始着手,因此有了下面这份代码。

产品类

// 比亚迪类
class BYD{
    public void name() {
        System.out.println("比亚迪");
    }
    public void price() {
        System.out.println("22万");
    }
    public void kind() {
        System.out.println("新能源");
    }
}
// 特斯拉类
class TSLA{
    public void name() {
        System.out.println("特斯拉");
    }
    public void price() {
        System.out.println("32万");
    }
    public void kind() {
        System.out.println("新能源轿车");
    }
}

客户端类

// 客户端调用代码
class Client {
    public static void main(String[] args) {
        BYD byd = new BYD();
        TSLA tsla = new TSLA();
    }
}

现在看着还算不错,两个品牌都有了自己的目标。所以他立刻把代码交给组长看。

普通写法遇到的问题

组长看过之后,对他说:
现在来说,只要两个品牌还好,可是后面的事务必定还要发展,品牌必定不止一个。
假如Client中有10个轿车品牌,100个轿车品牌该怎么办?难道一个Client类要和100个轿车品牌的类都有相关吗?迪米特法则——最少知道准则,难道你没听过吗?
一旦100个类中的一个类进行修正,就可能导致Client类呈现问题!
你去比照下规划准则,看下问题出在哪儿!!!

简略工厂形式的写法

小白,细心看了一遍代码,发现现在这种写法确实是耦合过于严重。Client类会和每个用到的轿车产品类发生相关。
不过好在能够用依赖反转 + 封装的办法修正原有代码,处理这个问题。

  1. 将轿车都有的属性或许办法办法抽离到轿车接口
  2. 一切的轿车产品都完成轿车接口。这样能够经过多态的特性,将Client类与轿车产品之间进行解耦。
  3. 将一切目标的创立过程封装到一个管理类中,这个类能够叫做Manager或许Factory。

简略工厂形式的代码

1.将公共的办法抽离到接口中
/**
 * @author jtl
 * @date 2021/7/20 16:18
 */
interface Car {
    void name();
    void price();
    void kind();
}
2.轿车品牌完成该接口
class BYD implements Car {
    @Override
    public void name() {
        System.out.println("比亚迪");
    }
    @Override
    public void price() {
        System.out.println("22万");
    }
    @Override
    public void kind() {
        System.out.println("新能源");
    }
}
class BMW implements Car{
    @Override
    public void name() {
        System.out.println("宝马");
    }
    @Override
    public void price() {
        System.out.println("40万");
    }
    @Override
    public void kind() {
        System.out.println("燃油车");
    }
}
class Tesla implements Car {
    @Override
    public void name() {
        System.out.println("特斯拉");
    }
    @Override
    public void price() {
        System.out.println("32万");
    }
    @Override
    public void kind() {
        System.out.println("新能源轿车");
    }
}
3.轿车的管理工厂类
class CarFactory {
    public static Car getCar(String name) {
        Car car =null ;
        switch (name){
            case "宝马":
                car = new BMW();
                break;
            case "特斯拉":
                car = new TSLA();
                break;
            case "比亚迪":
                car = new BYD();
                break;
            default:
                break;
        }
        return car;
    }
}
4.客户端调用
class Client {
    public static void main(String[] args) {
        Car car = CarFactory.getCar("比亚迪");
        Car car = CarFactory.getCar("特斯拉");
        Car car = CarFactory.getCar("宝马");
    }
}

简略工厂遇到的问题

经过简略工厂形式,让Client类只和Car还有CarFactory打交道,降低了耦合度,哪怕呈现1000个轿车品牌,也都和Client类没有关系了。

可是随之而来的是一个新的问题。每非必须新增轿车品牌,都要修正Factory类中的办法。这可是违反了规划准则中的最关键的一条准则之一,开闭准则,即对扩展开放对修正封闭

而且虽然Client类耦合度下来了,可是一切的轿车类都和Factory类有了相关。这仍旧违反迪米特法则——最少知道准则

工厂办法形式

正在小白束手无策的时分,组长走了过来对他说。你能想到这一步现已很出色了,我这儿有一份代码现已上传到GitLab上,里面用到了工厂办法形式。你拉取一下看看写法。随后不等小白说谢就回身离开了。小白把代码拉下来之后就看到了下面的代码。

正常办法完成的工厂办法

笼统产品代码
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:17
 * Detail(概况):笼统产品接口
 */
public interface ICar {
    void name();
    void price();
}
实际产品代码
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:20
 * Detail(概况):详细产品类----比亚迪电动车
 */
public class BYD implements ICar{
    @Override
    public void name() {
        System.out.println("比亚迪新能源电动车");
    }
    @Override
    public void price() {
        System.out.println("20W");
    }
}
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:21
 * Detail(概况):详细产品类---特斯拉电动车
 */
public class TSLA implements ICar{
    @Override
    public void name() {
        System.out.println("特斯拉新能源电动车");
    }
    @Override
    public void price() {
        System.out.println("28W");
    }
}
笼统工厂代码
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:18
 * Detail(概况):笼统工厂接口
 */
public interface IFactory {
    ICar buildCar();
}
实际工厂代码
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:23
 * Detail(概况):详细工厂类---比亚迪新能源工厂
 */
public class BYDFactory implements IFactory{
    @Override
    public ICar buildCar() {
        return new BYD();
    }
}
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:24
 * Detail(概况):详细工厂类---特斯拉上海工厂
 */
public class TSLAFactory implements IFactory{
    @Override
    public ICar buildCar() {
        return new TSLA();
    }
}
客户端调用代码
/**
 * Author(作者):jtl
 * Date(日期):2023/2/8 14:24
 * Detail(概况):工厂办法形式客户端
 */
public class Client {
    public static void main(String[] args) {
        IFactory bydFactory = new BYDFactory();
        bydFactory.buildCar().name();
        IFactory tslaFactory = new TSLAFactory();
        tslaFactory.buildCar().name();
    }
}

为了处理,简略工厂形式违反开闭准则迪米特法则——最少知道准则。组长对简略工厂进行了修正。

  1. 将轿车共有属性或许办法抽离到**轿车接口(ICar)**中
  2. 一切的轿车产品(TSLA,BYD等) 都完成轿车接口(ICar)。这样能够经过多态的特性,进行Client类与轿车产品之间的解耦
  3. 将创立轿车产品TSLA,BYD等) 的功能进行抽离封装到IFactory工厂接口
  4. 每一个轿车产品(TSLA,BYD等) 都有自己的完成了IFactory接口轿车工厂(TSLAFactory,BYDFactory等)

新的问题

虽然说,工厂办法形式处理了简略工厂中Factory类和各种轿车产品类耦合度过高的问题。而且再也不必,每次新增一个轿车产品就去简略工厂中修正代码了。

可是工厂办法形式有一个致命的问题就是,每新增一个轿车品牌就要增加一个轿车产品和轿车工厂。今天新增个五菱的轿车和五菱工厂,明日再新增个抱负的轿车和抱负的工厂。新增1000个轿车品牌就要新增1000个轿车和工厂。这样下去class的数量就会过多。这是一个不可忽视的问题。

经过反射完成的工厂办法

小白把他想到的问题,如数家珍的告知了组长。组长非常安慰的看着小白说,你能想到这个问题很好,说明你进步了。工厂办法形式确实会呈现这样的问题。因此我用反射写了一份工厂办法形式的代码,虽然说和轿车没关系,可是你能够类比一下。究竟不能什么都和轿车有关系啊!

笼统工厂代码

对应IFactory

public abstract class Factory {
   public abstract <T extends Product> T createProduct(Class<T> clazz);
}
笼统产品代码

对应ICar

public abstract class Product {
    abstract void method();
}
详细工厂代码

对应BYDFactory等轿车工厂

public class ConcreteFactory extends Factory{
    // 经过反射的办法创立详细工厂,处理了,一个详细工厂对应一个详细产品的问题
    @Override
    public <T extends Product> T createProduct(Class<T> clazz) {
        Product product = null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return (T) product;
    }
}
详细产品代码

对应BYD,TSLA等轿车产品

public class ConcreteProductA extends Product{
    @Override
    void method() {
        System.out.println("详细产品A");
    }
}
public class ConcreteProductB extends Product{
    @Override
    void method() {
        System.out.println("详细产品B");
    }
}
客户端代码完成
public class Client {
    public static void main(String[] args) {
        Factory factory = new ConcreteFactory();
        Product productA = factory.createProduct(ConcreteProductA.class);
        Product productB = factory.createProduct(ConcreteProductB.class);
        productA.method();//详细产品A
        productB.method();//详细产品B
    }
}

反射工厂办法中的妥协

经过反射的办法,处理了工厂办法形式中,过多的产品导致的产品工厂过多的问题。可是,Client类又和产品类有了一些耦合。只能说,没有十全十美的规划形式,只要合不适宜。

枯燥无味的定义

听完了故事,就来看一下,官方说法的定义和运用场景吧,

相关定义

工厂办法形式分为四个组成部分,笼统工厂,笼统产品,详细工厂,详细产品。
工厂办法形式属于创立型规划形式。是为了将目标的创立与Client类解耦。
工厂会回来目标给ClientClient不知道创立的详细细节。

运用场景

1. 有很多相同属性的同类型目标。
2. 需要将这些目标的创立对客户端进行解耦。

运用步骤

1. 将具有相同特征的目标的共同特征进行提取。生成笼统产品类。
2. 生成承继笼统产品的详细产品。
3. 创立笼统工厂,生成回来笼统产品的笼统办法。
4. 生成创立详细产品的详细工厂,该工厂承继笼统工厂。重写笼统办法,回来详细产品的目标。

注意事项

  1. 优点,将目标的创立与客户端代码别离完成解耦。
  2. 缺陷,每新增一个种类就要新增一个笼统产品和笼统工厂。
  3. 提升,能够用反射的办法完成详细工厂,而不是每有一个详细产品就创立一个与之相对应的详细工厂。能够减少代码量。

总结

至此,工厂办法形式就讲完了。简略工厂能够看作是工厂办法形式的一种。假如是简略的创立同类型目标的话,简略工厂形式就能够担任了。可是假如是大的项目,还是引荐运用工厂办法形式进行解耦。至所以运用正常的工厂办法,还是反射的工厂办法,那就完全看个人的选择了。究竟,只要适宜才是最好的

别的,工厂办法形式的运用前提是,很多的同类型的目标的创立,这点可千万别记错了。