设计模式之工厂模式

工厂模式

学习工厂设计模式,必须知道的相关概念

  1. 产品:

  2. 抽象产品:
    抽象类、接口

  3. 产品蔟
    多个有内在关系,或者是有逻辑关系的产品
    比如: 8mm螺丝与8mm螺母, 如果不是有关系的,根本就匹配不上

  4. 产品等级
    在这里插入图片描述


没有使用工厂模式:

//抽象产品
interface Food {
    void eat();
}

//具体产品
class Hamburger implements Food {

    @Override
    public void eat() {
        System.out.println("吃汉堡包!");
    }
}


//=======上面为服务端(提供服务,作者),下面为客户端(使用服务)================

public class AppTest {

    public static void main(String[] args) {
    	//创建对象
        Food f = new Hamburger();
        //调用方法
        f.eat();
    }
}
  • 这种设计相当脆弱! 为什么呢?

  • 因为 只要作者修改了具体产品的类名, 那么客户端代码,也要随之一起改变.
    这样服务端代码,和客户端代码就是耦合的!
    例如: 把作者把Hamburger改成Hburger(分割线上下是两个不同主体)

  • 我们希望的效果是,无论服务器端代码如何修改,客户端代码都应该不知道,不用修改客户端的代码!


工厂模式 – 简单工厂

//抽象产品
interface Food {
    void eat();
}


//具体产品
class Hamburger implements Food {

    @Override
    public void eat() {
        System.out.println("吃汉堡包!");
    }
}

class RiceNoodle implements Food {

    @Override
    public void eat() {
        System.out.println("过桥米线");
    }
}

//简单工厂
class FoodFactory {
    public static Food getFood(int n) {
        Food food = null;
        switch (n) {
            case 1:
                food = new Hamburger();
                break;
            case 2:
                food = new RiceNoodle();
                break;
        }
        return food;
    }
}


//=======上面为服务端(提供服务,作者),下面为客户端(使用服务)================

public class AppTest {

    public static void main(String[] args) {
       Food food = FoodFactory.getFood(1);

       food.eat();
    }
}
  • 这样看似已经解决了作者修改名字时,自己本身写的 FoodFactory 里面的case里面的名字也要修改.

  • 客户端(用户)只需要知道第1个是汉堡包,第2个是过桥米线就行

  • 简单工厂

    • 优点:
      1. 把具体产品的类型,从客户端代码中,解耦出来
      2. 服务端,如果修改了具体产品的类名,客户端也知道!
      • 这便符合了"面向接口编程"的思想
    • 缺点:
      1. 客户端不得不死记硬背那些常量于具体产品的映射关系,
        比如: 1对于汉堡包, 2对应米线
      2. 如果具体产品特别多, 则简单工厂,就会变得十分臃肿,
        比如有100个具体产品,则需要在简单工厂的switch中写出100个case!
      3. 最重要的是, 变化来了,客户端需要扩展具体产品的时候,势必要修改简单工厂中的代码,这样便违反了开闭原则
        但是有时我们并没有服务端的具体代码,而是字节文件,就算有也不能改!!!

工厂模式 – 工厂方法

  • 针对于简单工厂的问题
  • 修改代码如下, 使用工厂方法设计模式.
//抽象产品
interface Food {
    void eat();
}


//具体产品
class Hamburger implements Food {

    @Override
    public void eat() {
        System.out.println("吃汉堡包!");
    }
}

class RiceNoodle implements Food {

    @Override
    public void eat() {
        System.out.println("过桥米线");
    }
}


interface FoodFactory {
    public Food getFood();
}

class HamburgerFactory implements FoodFactory {
    @Override
    public Food getFood() {
        return new Hamburger();
    }
}

class RiceNoodleFactory implements FoodFactory {

    @Override
    public Food getFood() {
        return new RiceNoodle();
    }
}


class Business {
    public static void taste(FoodFactory ff) {
        Food food = ff.getFood();
        System.out.println("评委1,品尝");
        food.eat();

        Food food2 = ff.getFood();
        System.out.println("评委2,品尝");
        food2.eat();


        Food food3 = ff.getFood();
        System.out.println("评委3,品尝");
        food3.eat();
    }
}

//=======上面为服务端(提供服务,作者),下面为客户端(使用服务)================

public class AppTest {
    public static void main(String[] args) {

        FoodFactory ff = new HamburgerFactory();

        Business.taste(ff);
    }
}
  • 工厂方法:
    • 优点:

      1. 仍然具有简单工厂的优点,服务器修改了具体的类名以后,客户端不知道
      2. 当客户端需要扩展一个新的产品时,不需要修改作者原来的代码,只是扩展一个新的工厂而已!
    • 缺点:

      1. 如果有多个产品等级,那么工厂类的数量,就会爆炸式增长!
class Lp implements Food {
    @Override
    public void eat() {
        System.out.println("从小就吃凉皮");
    }
}

class LpFactory implements FoodFactory {

    @Override
    public Food getFood() {
        return new Lp();
    }
}

在这里插入图片描述
在这里插入图片描述

杠点

  • 杠点1:

    • 我们已经知道简单工厂也好,工厂方法也好, 都有一个优点,就是服务器端的具体产品类名变化以后,客户端不知道!
    • 但是,反观我们现在的代码,客户端依然依赖于具体的工厂的类名呀! 此时,如果服务器端修改了具体工厂的类名
    • 那么客户端也要随之一起修改!
    • 感觉折腾了一圈,又回到了原点
  • 解释1:

    • 工厂的名字,是被视为接口的.作者有责任,有义务,保证工厂的名字是稳定的
    • 也就说,虽然客户端依赖于工厂的具体类名
    • 可是在IT业内,所有工厂的名字都是趋向于稳定(并不是100%不会变). 至少工厂类的名字,要比具体产品类的名字更加稳定!
  • 杠点2:

    • 既然产品是我们客户端扩展出来的,那为什么不直接实例化呢?
    • 毕竟这个扩展出来的Lp这个产品,我们自己就是作者.
    • 我们想怎么改类名自己都能把控! 为什么还要为自己制作的产品做工厂呢?
  • 解释2:

    • 因为,作者在开发功能时,不仅仅只会开发一些抽象产品,具体产品、
    • 对应的工厂、还会搭配一些提前做好的框架
  • 杠点3:

    • 现在制作出LpFactory,是为了能把LpFactory传入到Business.taste方法,所有必须定义这个LpFactory
    • 那为什么不从一开始 就让Business.taste方法就直接接受Food参数呢?而不是现在FoodFactory作为参数
  • 解释3:

    • 那会又会回最初的问题: 只要作者修改了具体产品的类名, 那么客户端代码,也要随之一起改变

工厂模式 – 抽象工厂

  • 针对于工厂方法的问题,当有多个产品等级时(食物,饮料,甜品,…) 工厂类就会很多
  • 修改代码如下, 使用抽象工厂设计模式.
  • 抽象工厂就是从原本一个接口一个方法,变成一个接口多个方法
//抽象产品
interface Food {
    void eat();
}


//具体产品
class Hamburger implements Food {

    @Override
    public void eat() {
        System.out.println("吃汉堡包!");
    }
}

class RiceNoodle implements Food {

    @Override
    public void eat() {
        System.out.println("过桥米线");
    }
}

interface Drink {
    public void drink();
}

class Cola implements Drink {

    @Override
    public void drink() {
        System.out.println("可口可乐, 你值得拥有!");
    }
}

class IcePeak implements Drink {

    @Override
    public void drink() {
        System.out.println("从小就喝冰峰");
    }
}

interface Factory {
    public Food getFood();
    public Drink getDrink();
}

class KFCFactory implements Factory {

    @Override
    public Food getFood() {
        return new Hamburger();
    }

    @Override
    public Drink getDrink() {
        return new Cola();
    }
}

class Business {
    public static void taste(Factory ff) {

        Food food = ff.getFood();
        Drink drink = ff.getDrink();

        System.out.println("评委1,品尝");
        food.eat();
        drink.drink();

        Food food2 = ff.getFood();
        Drink drink2 = ff.getDrink();
        System.out.println("评委2,品尝");
        food2.eat();
        drink2.drink();


        Food food3 = ff.getFood();
        Drink drink3 = ff.getDrink();
        System.out.println("评委3,品尝");
        food3.eat();
        drink3.drink();
    }
}

//=======上面为服务端(提供服务,作者),下面为客户端(使用服务)================

//扩展
class Lp implements Food {

    @Override
    public void eat() {
        System.out.println("宝鸡擀面皮");
    }

}
class Fenta implements Drink {

    @Override
    public void drink() {
        System.out.println("芬达,你值得拥有!");
    }
}

class BaoJiFactory implements Factory {

    @Override
    public Food getFood() {
        return new Lp();
    }

    @Override
    public Drink getDrink() {
        return new Fenta();
    }
}

//运行
public class AppTest {
    public static void main(String[] args) {

        Business.taste(new KFCFactory());
    }
}

在这里插入图片描述

  • 抽象工厂
    • 优点:

      1. 仍然有简单工厂和工厂方法的优点
      2. 更重要的是,抽象工厂类的数量减少了, 无论有多少个产品等级,工厂就一套
    • 杠点:

      1. 为什么三秦工厂中,就必须米线搭配冰峰呢?为什么就不能是米线搭配可乐?
    • 解释:

      1. 抽象工厂中,可以生产多个产品,这多个产品之间,必须有内在联系.
      2. 同一个工厂中的产品都属于同一个产品蔟!不能把不同产品蔟的产品混合到一个抽象工厂的实现类中
    • 缺点:

      1. 当产品等级发生变化时,(增加产品等级,删除产品等级),都要引起所有以前工厂代码的修改.
        这就违反了"开闭原则"

结论:

  1. 当工厂不需要扩展,且数量不多时,可以使用简单工厂.,如果需要扩展,则不建议使用简单工厂
  2. 当需要扩展,并且数量不多时,可以使用工厂方法,当数量比较多时,不建议使用工厂方法
  3. 当产品等级比较固定,可以考虑使用抽象工厂, 如果产品等级经常变化,则不建议使用抽象工厂
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值