设计模式之工厂模式 | 8月更文挑战

648 阅读8分钟

image.png

欢迎来到今天的学习,今天我们将一起进行工厂模式的学习。再唠叨几句,前面我有提到本月将会对java的设计模式精讲,欢迎点击头像,关注我的专栏,我会持续更新,加油!

系列文章:

设计模式之单例模式

话不多话,进入正题

工厂模式

我记得一本书中曾说工厂模式被分为了三种:简单工厂、工厂方法和抽象工厂,目的只有一个:”抽象“。在实际工作中,用得比较多的可能就是工厂模式、和抽象工厂模式这两类。本文将主要讲这两个、简单工厂也会说一说(比较简单)。我们采用由浅到深,由易到难的方式层层探索。

下面先看张图很易于理解。

我们要做相应的算法运算,那么定义一个抽象的运算类,定义抽象的运算方法。该工厂便搭建起来了。那工厂总得生产产品吧,那继承之接口或实现类来实现各自不同的运算法则。 这便是非常经典的工厂模式 (所有的工厂方法模式系列都逃不出这张图

image.png

简单工厂模式

先从最简单的讲起吧。简单工厂,可不就很简单吗 哈哈。简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但好像不属于23种设计模式里。

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,也就是说定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。

image.png

  • 抽象类或接口:定义了要创建的产品对象的接口(抽象类或者接口都可以)
  • 具体实现:具有统一父类的具体类型的产品(ProductA...)
  • 产品工厂:负责创建产品对象。工厂模式同样体现了开闭原则,将“创建具体的产品实现类”这部分变化的代码从不变化的代码“使用产品”中分离出来,之后想要新增产品时,只需要扩展工厂的实现即可。

代码示例

我们设想这样一种场景。(做饼),有千层饼,酱香饼,还有葱花饼、鸡蛋饼......

那么我们要生产不同的饼类来满足大众的需要:


// 第一步 抽象类或接口:定义了要创建的产品对象的接口(抽象类或者接口都可以)
// 第二步 具体实现:具有统一父类的具体类型的产品(ProductA...)
// 第三步 产品工厂:负责创建产品对象。工厂模式同样体现了开闭原则,将“创建具体的产品实现类”这部分变化的代码从不变化的代码“使用产品”中分离出来,之后想要新增产品时,只需要扩展工厂的实现即可。


//定义接口()
public interface Cake {
    //生产饼
    void productCake();
}


//千层饼产品类
class ThousandCake implements Cake {

    @Override
    public void productCake() {
        //...输出逻辑,千层饼的做法;
    }
}

//葱花饼产品类
class ScallionCake implements Cake {

    @Override
    public void productCake() {
        //...输出逻辑,葱花饼的做法;
    }
}
//.....其他饼类省略



//第三步,创建工厂开始做饼

/**
 * 饼工厂
 */
public class CakeFactory {
    public Cake getInstance(int cakeType) {
        if(CakeEnum.QCB.getCode() == cakeType){
            //千层饼
            return new ThousandCake();
        } else if(CakeEnum.CHB.getCode() == cakeType){
             //葱花饼
            return new ScallionCake();
        }
        return null;
    }

    //调用
    public static void main(String[] args) {
        CakeFactory cakeFactory = new CakeFactory();
        //生产出千层饼
        Cake thousandCake = CakeFactory.getInstance(CakeEnum.QCB.getCode());
        //...
    }
}

上面的工厂实现是一个具体的类CakeFactory,而非接口或者抽象类,getInstance()方法利用if-else创建并返回具体的饼实例,如果增加新的饼子类,饼工厂的创建方法中就要增加新的if-else。如果再来酱香饼的做法该怎么办,还得写if else 这种做法扩展性差,违背了开闭原则,也影响了可读性。所以,这种方式使用在业务较简单,工厂类不会经常更改的情况。

那么针对上述情况有没有可改进的地方,往下看!

工厂方法模式

为了解决上面提到的"增加if-else"的问题,可以为每一个饼子类建立一个对应的工厂子类,这些工厂子类实现同一个抽象工厂接口。这样,创建不同种类的的饼,只需要实现不同的工厂子类。当有新种类加入时,新建具体工厂继承抽象工厂,而不用修改任何一个类。

image.png

可以看到和简单工厂相比,中间多加了为每个饼子类建立的对应的工厂类,对于根部的工厂类,这些类又叫工厂子类。

  • 抽象工厂:声明了工厂方法的接口。
  • 具体产品工厂:实现工厂方法的接口,负责创建产品对象。
  • 产品抽象类或接口:定义工厂方法所创建的产品对象的接口。
  • 具体产品实现:具有统一父类的具体类型的产品。

代码展示

// 第一步:抽象工厂:声明了工厂方法的接口。
// 第二步:具体产品工厂:实现工厂方法的接口,负责创建产品对象。
// 第三步:产品抽象类或接口:定义工厂方法所创建的产品对象的接口。
// 第四步:具体产品实现:具有统一父类的具体类型的产品。



/**
 * 首先无论是做千层饼还是做葱花饼,他们都有一个加工方法,可以抽象出来
 * Machine:机器
 */
public interface MachineApi {
    //process:加工      
    //material:材料
    public void process(String material); 
}

//第二步:创建具体产品工厂:实现工厂方法的接口,负责创建产品对象

/**
 * 千层饼机器
 * thousand cake : 千层饼
 */
public class ThousandCakeMachine implements MachineApi {
    @Override
    public void process(String material) {
        System.out.println("我把" + material + "加工成了千层饼");
    }
}


/**
 * 葱花饼机器
 * Scallion cake : 葱花饼
 */
public class ScallionCakeMachine implements MachineApi {
    @Override
    public void process(String material) {
        System.out.println("我把" + material + "加工成了葱花饼");
    }
}


//第三步:产品抽象类或接口:定义工厂方法所创建的产品对象的接口

public abstract class Factory{
    /**
     * 让子类(具体工厂)来实例化具体对象(机器)
     */
    public abstract MachineApi newMachine();
    
    /**
     * 加工材料
     */
    public void process(String material){
        MachineApi machine = newMachine();
        machine.process(material);
    }
}


//第四步:具有统一父类的具体类型的产品


//需要两家工厂,分别帮我生成千层饼和葱花饼
//千层饼工厂
public class ThousandCakeFactory extends Factory{
    //千层饼工厂,只需要提供千层饼机器就行
    @Override
    public MachineApi newMachine() {
        return new SteamedBunMachine();
    }
}

//葱花饼工厂
public class ScallionCakeFactory extends Factory{
    //葱花饼工厂,只需要提供葱花饼机器就行
    @Override
    public MachineApi newMachine() {
        return new BoodleMachine();
    }
}



ThousandCakeFactory cakeFactoryFactory  = new ThousandCakeFactory ();
cakeFactoryFactory.process("面粉");//我把面粉加工成了千层饼

每一种饼种类对应一个工厂子类,在创建具体饼对象时,实例化不同的工厂子类。但是,如果业务涉及的子类越来越多,难道每一个子类都要对应一个工厂类吗?这样会使得系统中类的个数成倍增加,增加了代码的复杂度。

接着往下看,抽象工厂!

抽象工厂

可能看到这里的小伙伴会说,这博主爱吃饼吧,怎么一直说饼,哈哈,那好,这个不说饼了,说说最近比较火的我们国产品牌“李宁” and “鸿星尔克”

image.png

大家看这幅图,类似这种把产品类分组,组内不同产品由同一工厂类的不同方法实现的设计模式,就是抽象工厂模式。

抽象工厂适用于以下情况:

  1. 一个系统要独立于它的产品的创建、组合和表示时;
  2. 一个系统要由多个产品系列中的一个来配置时;
  3. 要强调一系列相关的产品对象的设计以便进行联合使用时;
  4. 当你提供一个产品类库,而只想显示它们的接口而不是实现时;

代码展示

//衣服
public interface Clothes {
   void dressClothes();
}

//鸿星尔克品牌衣服
public class HXEKbrandClothes implements Clothes {
    @Override
    public void dressClothes() {
    }
}
//李宁品牌衣服
public class LiNBrandClothes implements Clothes {
    @Override
    public void dressClothes() {
    }
}

//鞋子
public interface Shoes {
   void dressShoes();
}

//穿有鸿星尔克品牌鞋子
public class HXEKbrandShoes implements Shoes {
    @Override
    public void dressShoes() {
        //穿着鸿星尔克牌
    }
}
//穿有李宁鞋子
public class LiNBrandShoes implements Shoes {
    @Override
    public void dressShoes() {
        //穿着李宁牌
    }
}



//裤子
public interface Pants {
   void dressPants();
}


//穿有鸿星尔克品牌裤子
public class HXEKbrandPants implements Pants {
    @Override
    public void dressPants() {
        //穿着鸿星尔克牌
    }
}
//穿有李宁裤子
public class LiNBrandPants implements Pants {
    @Override
    public void dressPants() {
        //穿着李宁牌
    }
}




//工厂类。工厂分为鸿星尔克工厂和李宁工厂,各自负责品牌内产品的创建
public interface IFactory {
    Pants createPants();
    Shoes createShoes();
    Clothes createClothes();
}

//鸿星尔克
public class HXEKFactory implements IFactory {
      @Override
      public Pants createPants(){
             Pants pants = new Pants();
             //造一条裤子
             return pants;
      }

      @Override
      public Shoes createShoes(){
             Shoes shoes = new Shoes();
             //造一双鞋子
             return shoes;
      }

      @Override
      public Clothes createClothes(){
             Clothes clothes = new Clothes();
             //...造上衣;
             return clothes;
      }
}


//李宁工厂
public class LiNFactory implements IFactory {
      @Override
      public Pants createPants(){
             Pants pants = new Pants();
             //造一条裤子
             return pants;
      }

      @Override
      public Shoes createShoes(){
             Shoes shoes = new Shoes();
             //造一双鞋子
             return shoes;
      }

      @Override
      public Clothes createClothes(){
             Clothes clothes = new Clothes();
             //...造上衣;
             return clothes;
      }
}

//客户端代码。实例化不同的工厂子类,可以通过不同的创建方法创建不同的产品
public class Main {
    public static void main(String[] args) {
        IFactory liNFactory = new LiNFactory();
        IFactory hxekFactory = new HXEKFactory();
        //创造鸿星尔克上衣
        Clothes hxekClothes = hxekFactory.createClothes();
        //...
    }
}

OK 本章结束,我们下期再见,看到这里,感谢您的阅读,如果觉得对您有帮助请 点赞收藏加关注!

我已经将本章收录在专题里,点击下方专题,关注专栏,我会每天发表干货,本月我会持续输入设计模式。

再次请求关注一下,创作不易,我这会儿写完已经10点半了,还在公司没有回家。加油! 我们下期再见