目录一. 摘要二. 需求案例(设计一个咖啡店的点餐系统)2.1 咖啡父类及其子类2.2 咖啡店类与咖啡类的关系三. 普通方法实线咖啡店点餐系统3.1 定义Coffee父类3.2 定义美式咖啡类继承Coffee类3.3 定义拿铁咖啡继承Coffee类3.4 定义咖啡店类3.5 编写测试类四. 简单工厂方法实线咖啡点餐系统4.1 简单工厂方法模式概述4.2 引入咖啡工厂4.3 修改 CoffeeStore 咖啡店类逻辑4.4 编写测试类4.5 简单工厂方法相较于普通方法的优点五. 工厂方法模式实线咖啡点餐系统5.1 分析简单工厂模式的缺点引出工厂方法模式5.2 引入抽象咖啡工厂5.3 修改咖啡工厂的代码5.4 编写测试类5.5 工厂方法模式的优缺点一. 摘要本篇文章主要讲述23种设计模式中的工厂方法模式。这里我们用一个咖啡店系统的小案例来引出简单工厂模式的使用在简单工厂模式的基础上延申介绍工厂方法模式。二. 需求案例(设计一个咖啡店的点餐系统)2.1 咖啡父类及其子类如图所示我们知道咖啡有很多种美式咖啡拿铁咖啡......所以在这个系统中不难发现咖啡类Coffee应该定义为父类又因为所有咖啡都会加糖加奶所以定义addMilk()addSugar()方法让子类继承即可此外咖啡都有不同的名字所以定义一个抽象方法getName()。然后让所有准确的咖啡类都继承我们的咖啡父类使用实线空心三角箭头表示继承关系2.2 咖啡店类与咖啡类的关系咖啡店可以点咖啡所以定义方法名为 orderCoffee方法参数就是顾客点的咖啡名称方法返回值就是顾客点的咖啡对象咖啡店类依赖咖啡类是用虚线箭头指向被依赖类Coffee。三. 普通方法实线咖啡店点餐系统这里我们先用最直接粗暴的方式实现上面的咖啡点餐系统非常简单主要分为以下几步3.1 定义Coffee父类public abstract class Coffee { // 加奶方法 public void addMilk() { System.out.println(add milk); } // 加糖方法 public void addSugar() { System.out.println(add sugar); } // 定义抽象方法获取咖啡名称由子类实现 public abstract String getName(); }3.2 定义美式咖啡类继承Coffee类public class AmericanCoffee extends Coffee{ Override public String getName() { return 美式咖啡; } }3.3 定义拿铁咖啡继承Coffee类public class LatteCoffee extends Coffee{ Override public String getName() { return 拿铁咖啡; } }3.4 定义咖啡店类public class CoffeeStore { public Coffee orderCoffee(String type) { Coffee coffee null; if (美式咖啡.equals(type)) { coffee new AmericanCoffee(); }else if (拿铁咖啡.equals(type)) { coffee new LatteCoffee(); }else { throw new RuntimeException(抱歉不支持这种咖啡); } coffee.addMilk(); coffee.addSugar(); return coffee; } }3.5 编写测试类public class TestCoffee { public static void main(String[] args) { // 创建咖啡店类 CoffeeStore coffeeShop new CoffeeStore(); // 点咖啡 Coffee coffee coffeeShop.orderCoffee(美式咖啡); System.out.println(coffee.getName()); } }运行方法可以看到方法执行已经成功达到了目的。但是这种做法有一个问题就是项目中类与类之间的耦合度太高了如果现在我再提一个新的需求。想要添加一个新的咖啡品种于此带来的影响就是咖啡店类中的orderCoffee方法逻辑需要重新做修改这样做就违背了Java中对修改关闭对扩展开发的的开发原则是非常不友好的。因此普通做法虽然简单粗暴能够以最快的速度达到目的却忽略了项目的可扩展性与程序的健壮性。四. 简单工厂方法实线咖啡点餐系统4.1 简单工厂方法模式概述简单工厂模式并不属于23种设计模式的一种但它在实际开发中也比较常用用的人多了就成了一种编程习惯恰好借此机会我们一起来看看简单工厂模式的做法。简单工厂主要包含以下几种角色1抽象产品定义了产品的规范描述了产品的主要特性和功能对应咖啡点餐系统中的Coffee父类父类中定义了加奶和加糖等共有属性2具体产品实现或继承了抽象产品的子类对应咖啡点餐系统中的美式咖啡AmericanCoffee拿铁咖啡LatteCoffee3具体工厂提供创建产品的方法调用者通过该方法获取产品。具体工厂就是简单工厂方法中我们要新增的工厂类。4.2 引入咖啡工厂如下图所示我们做项目总是调侃一句话没有什么问题是加一层无法解决的如果解决不了就加两层......在简单工厂方法中我们就需要在Coffee咖啡类和CoffeStore咖啡店中间加一层创建 SimpleCoffeeFactory 咖啡工厂类由咖啡工厂负责生产咖啡当咖啡店有人点餐时直接调用咖啡工厂的 createCoffee() 创建咖啡方法方法返回值为Coffee。对比普通普通方法我们需要创建SimpleCoffeeFactory咖啡工厂类再将CoffeeStore咖啡店类中点咖啡方法做修改如下所示SimpleCoffeeFactor 咖啡工厂类public class SimpleCoffeeFactory { public Coffee createCoffee(String type) { Coffee coffee null; if (美式咖啡.equals(type)) { coffee new AmericanCoffee(); }else if (拿铁咖啡.equals(type)) { coffee new LatteCoffee(); }else { throw new RuntimeException(抱歉不支持这种咖啡); } // 这里我们生产完毕咖啡直接返回将加糖或加奶的决定权交给顾客 return coffee; } }4.3 修改 CoffeeStore 咖啡店类逻辑可以看到我们将咖啡类和咖啡店类进行解耦让咖啡工厂作为二者的中间桥梁如果后续我们要添加其他品种的咖啡直接修改咖啡工厂的代码逻辑即可咖啡类Coffee和咖啡店类CoffeeStore都不会受到任何影响。而且我们给了顾客选择顾客只想加糖就调用 orderCoffeeOnlySugar 方法顾客只想加奶就调用 orderCoffeeOnlyMilk 方法如果都不想加可以再创建另外一个方法极大地简化了代码量。public class CoffeeStore { // 创建一个咖啡工厂的对象 SimpleCoffeeFactory factory new SimpleCoffeeFactory(); public Coffee orderCoffeeOnlySugar(String type) { Coffee coffee factory.createCoffee(type); coffee.addSugar(); return coffee; } public Coffee orderCoffeeOnlyMilk(String type) { Coffee coffee factory.createCoffee(type); coffee.addMilk(); return coffee; } }4.4 编写测试类只需要创建 SimpleCoffeeFactory 咖啡工厂对象我们就可以调用咖啡工厂对象的方法传入我们希望得到的咖啡此时咖啡类和咖啡店类就完成了解耦合。public static void main(String[] args) { SimpleCoffeeFactory factory new SimpleCoffeeFactory(); // 点咖啡 Coffee coffee factory.createCoffee(拿铁咖啡); System.out.println(coffee.getName()); System.out.println(--------------------------------); Coffee coffee1 factory.createCoffee(美式咖啡); System.out.println(coffee1.getName()); }4.5 简单工厂方法相较于普通方法的优点如果我们采用最原始的方法点一杯只加糖和只加奶的咖啡代码逻辑如下可以看到每当用户点一杯咖啡我们就要写一次咖啡的判断逻辑并创建咖啡非常麻烦。因此我们就可以将公共的判断咖啡种类和创建咖啡的公共部分抽取出来交给咖啡工厂去完成这样一来就可以节省大量代码使项目中各部分的代码耦合度降低public class CoffeeStore { public Coffee orderCoffeeOnlySugar(String type) { Coffee coffee null; if (美式咖啡.equals(type)) { coffee new AmericanCoffee(); }else if (拿铁咖啡.equals(type)) { coffee new LatteCoffee(); }else { throw new RuntimeException(抱歉不支持这种咖啡); } coffee.addSugar(); return coffee; } public Coffee orderCoffeeOnlyMilk(String type) { Coffee coffee null; if (美式咖啡.equals(type)) { coffee new AmericanCoffee(); }else if (拿铁咖啡.equals(type)) { coffee new LatteCoffee(); }else { throw new RuntimeException(抱歉不支持这种咖啡); } coffee.addMilk(); return coffee; }五. 工厂方法模式实线咖啡点餐系统5.1 分析简单工厂模式的缺点引出工厂方法模式刚才我们使用了简单工厂方法实线了点咖啡的案例但我们也可以发现这种方法的缺点就是仍然违背了 对修改开发对扩展封闭 的原则。使用了咖啡工厂之后我们只需要将生产咖啡交给工厂来完成。但是如果我们要增加一种新的咖啡还是需要修改咖啡工厂中的代码逻辑我们将新需求带来的影响缩小到了咖啡工厂这个类中但还是需要做修改违背了开闭原则。但如果我们使用工厂方法模式就不会违背开闭原则它的做法是定义一个创建对象的接口让子类决定实例化哪种产品。工厂方法模式的主要角色1抽象工厂(Abstract Factory)提供创建产品的接口调用者通过它访问具体工厂的工厂方法来创建产品。2具体工厂(Concreate Factory)主要实现抽象工厂中的抽象方法完成产品的创建。3抽象产品(Abstract Product)定义产品的规范描述了产品的主要功能和特性。4具体产品(Concreate Product)实线了抽象产品角色定义的接口由具体工厂来创建它同具体工厂之间一一对应。5.2 引入抽象咖啡工厂在简单工厂方法模式中我们将生产咖啡提取成一个工厂让工厂生产咖啡。但实际上咖啡工厂仍然可以做进一步的抽象让子类去实线抽象工厂中的方法。这样一来当我们需要新增一种咖啡时只需要新增一个咖啡工厂的实现类其他的都不需要做任何改变。5.3 修改咖啡工厂的代码对比简单工厂抽象咖啡类Coffee子类AmericanCoffee和LatteCoffee都不用改变新建抽象咖啡工厂类public interface CoffeeFactory { // 创建咖啡对向的方法 AmericanCoffee createCoffee(); }创建美式咖啡工厂类实线抽象咖啡工厂接口此工厂专门生产美式咖啡public class AmericanCoffeeFactory implements CoffeeFactory{ Override public AmericanCoffee createCoffee() { return new AmericanCoffee(); } }创建拿铁咖啡工厂类实线抽象咖啡工厂接口此工厂专门生产拿铁咖啡public class LatteCoffeeFactory implements CoffeeFactory{ Override public LatteCoffee createCoffee() { return new LatteCoffee(); } }修改咖啡店类点咖啡的代码逻辑这里几乎没有很大变化关键点在于创建的咖啡工厂对象为顶层父接口对象我们只需要通过父接口对象调用 createCoffee() 对象public class CoffeeStore { private CoffeeFactory coffeeFactory; public void setCoffeeFactory(CoffeeFactory coffeeFactory) { this.coffeeFactory coffeeFactory; } public Coffee createCoffee(String orderType) { Coffee coffee coffeeFactory.createCoffee(); coffee.addMilk(); coffee.addSugar(); return coffee; } }5.4 编写测试类public static void main(String[] args) { // 创建咖啡店对象 CoffeeStore store new CoffeeStore(); // 创建拿铁咖啡工厂对象父类引用指向子类对象 CoffeeFactory latteCoffeeFactory new LatteCoffeeFactory(); store.setCoffeeFactory(latteCoffeeFactory); // 点咖啡 Coffee coffee store.createCoffee(拿铁咖啡); System.out.println(coffee.getName()); }运行测试类我们就会得到拿铁咖啡工厂生产的拿铁咖啡5.5 工厂方法模式的优缺点优点实线了代码之间的解耦模块之间耦合度降低。当系统要添加新的产品类时只需要添加具体产品类和具体工厂类无需对原有工厂作出修改满足开闭原则。举例当我们想要增加新品种的咖啡时(比如香草咖啡)只需要在创建一个香草咖啡工厂去实现咖啡工厂接口再创建一个香草咖啡类继承咖啡父类不需要对以往的代码作出修改只在原有的代码上做增加。缺点每增加一种产品就需要增加一个产品工厂类和一个具体产品类随着产品越来越多会导致系统中的代码越来越多越来越复杂增加了系统的复杂度不易维护。