具体咖啡
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 12:51 * @PackageName:com.jsxs.pattern.factory.before * @ClassName: LatteCoffe * @Description: TODO 拿铁咖啡 * @Version 1.0 */ public class LatteCoffee extends Coffee { @Override public String getName() { return "拿铁咖啡"; } }
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 12:50 * @PackageName:com.jsxs.pattern.factory.before * @ClassName: AmericanCoffee * @Description: TODO 美式咖啡 * @Version 1.0 */ public class AmericanCoffee extends Coffee { @Override public String getName() { return "美式咖啡"; } }
具体甜品
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:21 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: Trmisu * @Description: TODO 提拉米苏类 * @Version 1.0 */ public class Tiramisu extends Dessert{ @Override public void show() { System.out.println("提拉米苏-意大利风格"); } }
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:23 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: MatchaMousee * @Description: TODO 抹茶慕斯类 * @Version 1.0 */ public class MatchaMousee extends Dessert{ @Override public void show() { System.out.println("抹茶慕斯-美式风格"); } }
抽象大工厂
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:24 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: DessertFactory * @Description: TODO * @Version 1.0 */ public interface DessertFactory { // 1.生产咖啡 Coffee createCoffee(); // 2.生产甜品 Dessert createDessert(); }
美式套餐
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:25 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: AmericanFactoryImp * @Description: TODO 美式风格工厂 * @Version 1.0 */ public class AmericanFactoryImp implements DessertFactory{ @Override public Coffee createCoffee() { return new AmericanCoffee(); // 美式咖啡 } @Override public Dessert createDessert() { return new MatchaMousee(); //抹茶慕斯 } }
意大利套餐
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:27 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: LatteFactoryImp * @Description: TODO * @Version 1.0 */ public class LatteFactoryImp implements DessertFactory{ @Override public Coffee createCoffee() { return new LatteCoffee(); } @Override public Dessert createDessert() { return new Tiramisu(); //提拉米苏 } }
咖啡店
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:28 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: CofeStore * @Description: TODO * @Version 1.0 */ public class CoffeeStore { private DessertFactory dessertFactory; public void setDessertFactory(DessertFactory dessertFactory) { this.dessertFactory = dessertFactory; } public void Order(){ Coffee coffee = dessertFactory.createCoffee(); Dessert dessert = dessertFactory.createDessert(); System.out.println(coffee.getName()); coffee.addMilk(); coffee.addSuger(); dessert.show(); } }
客户
package com.jsxs.pattern.factory.abstract_factory; /** * @Author Jsxs * @Date 2023/4/17 16:28 * @PackageName:com.jsxs.pattern.factory.abstract_factory * @ClassName: CofeStore * @Description: TODO * @Version 1.0 */ public class CoffeeStore { private DessertFactory dessertFactory; public void setDessertFactory(DessertFactory dessertFactory) { this.dessertFactory = dessertFactory; } public void Order(){ Coffee coffee = dessertFactory.createCoffee(); Dessert dessert = dessertFactory.createDessert(); System.out.println(coffee.getName()); coffee.addMilk(); coffee.addSuger(); dessert.show(); } }
如果我们需要再添加套餐(产品族)的话,只需要再加一个对应的工厂类即可,不需要再修改其他的类。
(4).优缺点
优点:
当一个产品族(套餐)中的多个对象被设计成一起工作的时候,它能保证客户端始终只使用同一个产品族中的对象 (套餐的对象必定一定)。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改
。eg:美式套餐中我们赠送一个意大利风格的甜品。(不能赠送或新增单个产品,可以新增一整个套餐)
(5).使用场景
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视、洗衣机、空调等。
- 系统中有多个产品族。但每次只使用其中的某一族产品。如有人只喜欢穿一个指定的套餐(固定的产品族)
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实列的创建细节和内部结构。如: 输入法换皮肤,一整套一整套的换。生成不同的操作系统程序。
(6).JDK源码解析- Collection.iterator方法
使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。
5.工厂模式的扩展💔 -(这个不属于23种)
简单工厂+配置文件解除耦合
可以通过工厂模式+配置文件
的方式解除工厂对象和产品对象
的耦合。在工厂中加载配置文件中的全类名,并创建对象和存储,客户端如果需要对象对象,直接进行获取即可。
beans.properties: 配置文件
american=com.jsxs.pattern.factory.config_factory.AmericanCoffee latte=com.jsxs.pattern.factory.config_factory.LatteCoffee
咖啡抽象类
package com.jsxs.pattern.factory.config_factory; /** * @Author Jsxs * @Date 2023/4/17 13:54 * @PackageName:com.jsxs.pattern.factory.Single_Factory * @ClassName: Coffee * @Description: TODO * @Version 1.0 */ public abstract class Coffee { // 0. 子类必须要实现抽象方法 public abstract String getName(); // 1.加糖 public void addSuger() { System.out.println("加糖"); } // 2.加奶 public void addMilk() { System.out.println("加奶"); } }
具体实现
package com.jsxs.pattern.factory.config_factory; /** * @Author Jsxs * @Date 2023/4/17 12:50 * @PackageName:com.jsxs.pattern.factory.before * @ClassName: AmericanCoffee * @Description: TODO 美式咖啡 * @Version 1.0 */ public class AmericanCoffee extends Coffee { @Override public String getName() { return "美式咖啡"; } }
package com.jsxs.pattern.factory.config_factory; /** * @Author Jsxs * @Date 2023/4/17 12:51 * @PackageName:com.jsxs.pattern.factory.before * @ClassName: LatteCoffe * @Description: TODO 拿铁咖啡 * @Version 1.0 */ public class LatteCoffee extends Coffee { @Override public String getName() { return "拿铁咖啡"; } }
咖啡工厂类
package com.jsxs.pattern.factory.config_factory; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Properties; import java.util.Set; /** * @Author Jsxs * @Date 2023/4/17 17:50 * @PackageName:com.jsxs.pattern.factory.config_factory * @ClassName: CoffeeFactory * @Description: TODO 咖啡工厂类 * @Version 1.0 */ public class CoffeeFactory { // 1. 加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储 //2.定义容器对象存储咖啡对象 private static HashMap<String,Coffee> map=new HashMap<String,Coffee>(); //3.加载配置文件,只需要加载一次 static { //3.1创建Properties对象 Properties properties = new Properties(); //3.2调用load方法进行加载 InputStream in = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties"); //获取输入流、读取哪个文件 try { properties.load(in); //这个参数是: 输入流。加载完毕之后,这个properties就已经存在数据了。 //3.3从properties集合中获取全类名创建对象 Set<Object> keys = properties.keySet(); // 获取配置文件中全部的键 for (Object key : keys) { String ClassName = properties.getProperty((String) key); // 通过获取到配置文件中的键,我们得到值。 //3.4 通过反射去创建对象 Class aClass = Class.forName(ClassName); // 通过加载类全民获取字节码对象 Coffee coffee = (Coffee) aClass.newInstance(); //通过无参构造创建对象 // 将名称和对象那个存储到我们自定义的容器中 map.put((String) key,coffee); } } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } // 根据名称获取对象 public static Coffee createCoffee(String type){ return map.get(type); } }
package com.jsxs.pattern.factory.config_factory; /** * @Author Jsxs * @Date 2023/4/17 18:09 * @PackageName:com.jsxs.pattern.factory.config_factory * @ClassName: Client * @Description: TODO * @Version 1.0 */ public class Client { public static void main(String[] args) { Coffee coffee = CoffeeFactory.createCoffee("latte"); System.out.println(coffee.getName()); } }
静态成员变量用来存储创建的对象(键存储的是名称、值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需执行一次
。