一:工厂方法模式的适用的场景

工厂方法模式是一个具体工厂只生产一类产品。
现在咖啡店升级了不止卖咖啡了也开始卖甜点了,咖啡和甜点是两种不同的产品,如果我们按照工厂方法模式该怎么做呢?首先无论使用什么模式Dessert(甜点)、Trimisu(提拉米苏)、MatchMousse(抹茶慕斯) 三个实体肯定是少不了的,因为增加两个具体的甜品所以也要相应的增加两天具体的甜品工厂MatchMousseDessertFactory、TrimisuDessertFactory,相应的CoffeeStore也要增加orderDessert()方法。

// 甜点父类
public abstract class Dessert {
    public abstract String getName();
}

public class Trimisu extends Dessert {
    @Override
    public String getName() {
        return "提拉米苏(意大利风味)";
    }
}

public class MatchMousse extends Dessert {
    @Override
    public String getName() {
        return "抹茶慕斯(美式风味)";
    }
}

// 增加一个甜点工厂
public interface DessertFactory {
    Dessert createDessert();
}

// 增加一个抹茶慕斯工厂
public class MatchMousseDessertFactory implements DessertFactory {
    @Override
    public Dessert createDessert() {
        return new MatchMousse();
    }
}

// 增加一个提拉米苏工厂
public class TrimisuDessertFactory implements DessertFactory {
    @Override
    public Dessert createDessert() {
        return new Trimisu();
    }
}
public class CoffeeStore {

    private CoffeeFactory coffeeFactory;
    // 新增甜点工厂
    private DessertFactory dessertFactory;

    public void setCoffeeFactory(CoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public void setDessertFactory(DessertFactory dessertFactory) {
        this.dessertFactory = dessertFactory;
    }

    public Coffee orderCoffee() {
        Coffee coffee = coffeeFactory.createCoffee();
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }
    
    // 增加点甜品功能
    public Dessert orderDessert() {
        Dessert dessert = dessertFactory.createDessert();
        System.out.println("制作甜点");
        return dessert;
    }
}
public class ItalyClient {
    public static void main(String[] args) {
        CoffeeFactory coffeeFactory = new LatteCoffeeFactory();
        // 新增提拉米苏工厂
        DessertFactory dessertFactory = new TrimisuDessertFactory();

        CoffeeStore coffeeStore = new CoffeeStore();
        coffeeStore.setCoffeeFactory(coffeeFactory);
        coffeeStore.setDessertFactory(dessertFactory);

        Coffee coffee = coffeeStore.orderCoffee();
        // 点甜点
        Dessert dessert = coffeeStore.orderDessert();
        System.out.println(coffee.getName());
        System.out.println(dessert.getName());
    }
}

现在是增加了2个甜点需要创建2个工厂,如果增加100个甜点就要增加100个工厂类,这样会造成类文件过多(类爆炸)。

二:抽象工厂要解决的问题

工厂方法模式是一个工厂只生产一种特定的产品,实际社会中很多工厂都会生产多个产品(如咖啡店可能买咖啡、甜点、简餐等)。我们能不能把“一个工厂只生产一种特定产品”再进行抽象一下,让一个工厂可以生产多个产品,并且这个工厂生产的所有产品都有一定的关联性。

比如开一个美式风味工厂可以生产美式咖啡、美式甜点、美式简餐,再开一个意大利风味工厂可以生产意大利式咖啡、意大利式甜点、意大利式简餐。

工厂方法模式和抽象工厂模式其实就是分类颗粒度的程度不同,工厂方法模式分类是分的特别细一类产品就属于一个工厂,而抽象工厂模式是以系列作为分类,分类的范围更广泛。

这样由原来的工厂方法模式(如果新增100个产品增加100个具体工厂类)就变成了抽象工厂模式(如果新增100个产品,就变成只需要2个工厂类(美式风味工厂、意大利风味工厂)即可)。

public interface CategoryFactory {
    Coffee createCoffee();
    Dessert createDessert();
}

// 美式风味工厂
public class AmericanFactory implements CategoryFactory {
    @Override
    public Coffee crateCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchMousse();
    }
}

// 意大利风味工厂
public class ItalyFactory implements CategoryFactory {
    @Override
    public Coffee crateCoffee() {
        return new LatteCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new Trimisu();
    }
}
public class CoffeeStore {
	// 系列工厂
    private CategoryFactory categoryFactory;

    public void setCategoryFactory(CategoryFactory categoryFactory) {
        this.categoryFactory = categoryFactory;
    }

    public Coffee orderCoffee() {
        Coffee coffee = categoryFactory.createCoffee();
        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }

    public Dessert orderDessert() {
        Dessert dessert = categoryFactory.createDessert();
        System.out.println("制作甜点");
        return dessert;
    }
}
public class Client {
    public static void main(String[] args) {
        // 只需要创建一个系列工厂,就能获取该系列工厂的所有产品
        CategoryFactory factory = new ItalyFactory();
        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();
    }
}

三:抽象工厂的应用场景

抽象工厂模式是工厂方法模式的升级版本,解决了工厂方法设计模式的类爆炸问题,同时也改变了应用场景,抽象工厂模式是为了解决一系列相关产品的创建。比如如果我偏好喜欢意大利口味的,我只需要创建一个意大利风味的工厂,就能得到这个工厂生产所有意大利口味的东西(咖啡、甜点、面条等)。

例如比如数据库驱动如果配置成mysql,就会创建mysql相关的所有对象如MysqlConnecton、MysqlSession、MySqlDataSource等,如果是oracle也会创建oracle相关的对象。

四:抽象工厂的优缺点

  • 优点:用于一次创建一系列对象的工厂,解决了工厂方法的类爆炸问题。
  • 缺点:每增加一个具体的对象都需要在每个工厂中添加对应的实现。比如咖啡店要卖简餐了,那所有的工厂都要增加一个方法。
public interface CategoryFactory {
    Coffee createCoffee();
    Dessert createDessert();
    SimpleFood createSimpleFood();
}


public class ItalyFactory implements CategoryFactory {
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new Trimisu();
    }

	// 意大利工厂要增加简餐
    @Override
    public SimpleFood createSimpleFood() {
        return null;
    }
}


public class AmericanFactory implements CategoryFactory {
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchMousse();
    }

	// 美式工厂也要增加简餐
    @Override
    public SimpleFood createSimpleFood() {
        return null;
    }
}