Loading

工厂模式

工厂模式

七大设计原则

工厂模式中的相关概念

  1. 产品:类
  2. 抽象产品:抽象类,接口
  3. 产品簇:多个有内在联系,或者是有逻辑关系的产品。比如6mm的螺丝和6mm的螺母在一个产品簇中,6mm螺丝和8mm的螺母就不是一个产品簇。
  4. 产品等级:

image-20210310082501866

简单工厂模式

案例

// 作者
interface Food{
    void eat();
}

class HotDog implements Food{

    @Override
    public void eat() {
        System.out.println("吃热狗");
    }
}

class Apple implements Food{

    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}
// 客户
public class NegativeDesign {
    public static void main(String[] args) {
        Food hotDog=new HotDog();
        hotDog.eat();
        Food apple=new Apple();
        apple.eat();
    }
}

上述代码中,当实现类修改的时候,比如修改了类名称,客户是不知道的,一旦作者修改了类名称,那么客户的项目将会崩塌。

简单工厂

// 作者
// 抽象产品
interface SFood {
    void eat();
}

// 具体产品
class SHotDog implements Food {

    @Override
    public void eat() {
        System.out.println("吃热狗");
    }
}

class SApple 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 SHotDog();
                break;
            case 2:
                food = new SApple();
                break;
        }
        return food;
    }
}

public class SimpleFactory {
    public static void main(String[] args) {
        Food hotDog = FoodFactory.getFood(1);
        hotDog.eat();
    }
}

简单工厂的优点:

  1. 把具体产品的类型,从客户端代码中,解耦出来。
  2. 服务器端,如果修改了具体的类名,客户端不需要知道。
  3. 更加符合了“面向接口编程“的思想。

缺点:

  1. 客户端不得不记住那些与具体常量映射的常量,比如:1对应热狗,2对应苹果。
  2. 如果具体产品特别多,则简单工厂会变得十分臃肿。比如有100个具体产品,则需要在简单工厂中写100个case。
  3. 最重要的是,当需求或者变化来的时候,客户端需要拓展具体产品的时候,势必要修改简单工厂中的代码,这样就违反了“开闭原则”。

简单工厂模式的UML

image-20210308210214828

工厂方法模式

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

// 产品
class HotDog implements Food {

    @Override
    public void eat() {
        System.out.println("吃热狗");
    }
}

class Apple implements Food {

    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}

// 新增产品
class iceCream implements Food {

    @Override
    public void eat() {
        System.out.println("吃冰淇淋");
    }
}

interface FoodFactory {
    public Food getFood();
}

class HotDogFactory implements FoodFactory {

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

class AppleFactory implements FoodFactory {

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

class IceCreamFactory implements FoodFactory {

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

// 和事先准备好的框架配套使用
class Business{
    public static void taste(FoodFactory ff){
        Food f = ff.getFood();
        System.out.println("评委1,品尝");
        f.eat();

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

        Food f3 = ff.getFood();
        System.out.println("评委3,品尝");
        f3.eat();
    }
}
/*
// 如果修改成这样的,那么调用该方法的时候,传入的就是食物类,就又会出现服务端的类名称修改了而导致客户端出错
class Business {
    public static void taste(Food food) {
        System.out.println("评委1,品尝");
        food.eat();

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

        System.out.println("评委3,品尝");
        food.eat();
    }
}*/

public class FactoryMethodPattern {
    public static void main(String[] args) {
        // 这是当新增一个产品的时候,就不需要修改以前的代码,只需要再建立一个该产品的工厂就可以了
        FoodFactory foodFactory = new IceCreamFactory();
        Food food = foodFactory.getFood();
        food.eat();
    }

}

优点

  1. 仍然具有简单工厂的优点,服务端修改了具体产品的类名以后,客户端是不知道的。

  2. 当客户端需要拓展一个新的产品时,不需要修改作者原来的代码,只是拓展一个新的工厂而已。

抬杠

  1. 我们已经知道,简单工厂也好,工厂方法也好,都有一个优点,,就是服务器端的具体产品的类名变化以后,客户端是不需要知道的。

    但是,反观我们现在的代码,客户端仍然需要依赖于具体的工厂的类名呀。此时,如果服务器端修改了具体工厂的类名,那么客户端耶要随之一起修改。

    解释:

    工厂的名字,是视为接口的,作者有责任,有义务,保证工厂的名字是稳定的。耶就是说,虽然客户端依赖于工厂的具体类名,可是在生产和开发中,所有工厂的名字都是趋于稳定的,至少工厂类的名字,要比具体产品雷的名字更加稳定。

  2. 既然产品是我们客户端拓展出来的,那为什么不直接自己实例化呢?毕竟这个拓展出来的IceCream这个产品,我们自己就是作者,我们想怎么改类名就改类名,为什么还要自己制作工厂呢?

    解释:

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

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

    解释:

    如果修改成Food作为参数,那么调用该方法的时候,传入的就是食物类,就又会出现服务端的类名称修改了而导致客户端出错。但用工厂的话,工厂一般很少进行修改。

工厂方法模式的UML

image-20210309175938016

缺点

但出现多个产品簇的时候,使用工厂方法模式会产生类爆炸。

抽象工厂

// 抽象的工厂,什么都可以生产
interface Factory {
    public Food getFood();
    public Drink getDrink();
}

class KFCFactory implements Factory {

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

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

class FruitFactory implements Factory {

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

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

class IceCreamFactory implements Factory {

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

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

// 和事先准备好的框架配套使用
class Business {
    public static void taste(Factory ff) {
        Food f = ff.getFood();
        Drink drink = ff.getDrink();
        System.out.println("评委1,品尝");
        drink.drink();
        f.eat();

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

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

public class AbstractFactory {
    public static void main(String[] args) {
        Factory kfcFactory = new KFCFactory();
        Business.taste(kfcFactory);
    }
}

抽象工厂优点

  1. 仍然具有简单工厂和工厂方法的优点
  2. 抽象工厂把工厂类的数量减少了,无论有多少个产品等级,工厂就一套。

抬杠

为什么在KFC工厂中,就必须是可乐+热狗呢?为什么不能是可乐+苹果呢?

解释:

在抽象工厂中,可以生产多个产品,这多个产品之间,必须要有内置联系。

同一个工厂中的产品属于同一个产品簇!!不能把不同产品簇中的产品混合到一个抽象工厂中的实现类去。

缺点

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

结论

  1. 如果产品中的产品等级固定时,可以考虑抽象工厂。
  2. 如果产品簇经常变化时,则不建议使用抽象工厂。
posted @ 2021-03-10 08:44  nuoxin  阅读(73)  评论(0)    收藏  举报