Spring 设计模式之工厂模式

在软件开发领域,设计模式 是解决常见问题的最佳实践。
Spring 框架作为 Java 生态中的佼佼者,其成功在很大程度上 归功于对设计模式的巧妙运用。
“Spring 中用到了哪些设计模式?”,这个问题,在 面试 中也比较常见,在此进行整理。

用到的场景

要基于 不同条件(大量的if-else或switch-case)创建不同对象的情况时。可以考虑使用工厂模式

一、简单工厂模式(静态工厂模式)

简单工厂模式又叫静态工厂方法模式,就是建立一个工厂类(是类的形式,而不是接口),对实现了同一接口的一些类进行实例的创建。
其核心是 由一个工厂类根据传入的参数,动态决定创建哪一个产品类的实例。

优点

  1. 责任分割:工厂类包含判断逻辑,决定何时创建哪个产品实例,客户端仅负责“消费”产品。
  2. 灵活性:根据外界信息决定创建哪个具体对象,客户端无需直接创建对象。
  3. 低耦合:外界与具体产品类隔离,降低耦合性。
  4. 结构优化:明确区分职责和权力,利于软件体系结构优化。

缺点

  1. 扩展困难:增加或删除产品需修改工厂逻辑,系统扩展性受限。
  2. 违背OCP:修改工厂逻辑可能导致过于复杂,违背开放-封闭原则(对新增开放,对修改关闭)。
  3. 无法继承:静态工厂方法使得工厂角色无法形成基于继承的等级结构。

Spring框架中的体现

举例说明

您走进了一家名为“万能奶茶店”的饮品店。这家店里只有一位店员(相当于工厂类),他负责制作店里所有种类的奶茶。当您告诉店员您想喝的奶茶类型(比如珍珠奶茶、抹茶拿铁或草莓奶茶)时,店员会根据您的要求为您精心制作一杯(即创建奶茶对象)。

然而,如果这家奶茶店想要推出新口味的奶茶(比如芒果奶茶),店员就需要学习新口味的制作方法,并且还要在菜单上添加这个新选项。这就意味着店员(即工厂类)的职责范围需要扩大,因此其代码也需要相应地进行修改,以支持这种新口味的奶茶。

这种模式下,每当有新产品加入时,都需要对工厂类进行修改,这可能会影响到系统的稳定性和可维护性。

Java代码实现

下面是一个使用简单工厂模式来实现上述场景的Java示例。

知识小贴士:
咖啡对象也可以用类来创建,但是如果是类的话,就必须把咖啡类定义为抽象类(public abstract class MilkTea )是为了强制子类实现必要的方法,防止直接实例化,以及(可选地)提供共享代码。

// 奶茶类,作为所有奶茶的基类  
interface MilkTea {
    
      
    // 制作奶茶的方法  
    public abstract void make();  
}  
  
// 珍珠奶茶类  
class PearlMilkTea implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作珍珠奶茶");  
    }  
}  
  
// 抹茶拿铁类  
class MatchaLatte implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作抹茶拿铁");  
    }  
}  
  
// 草莓奶茶类  
class StrawberryMilkTea implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作草莓奶茶");  
    }  
}  
  
// 简单工厂类,负责创建所有种类的奶茶  
class SimpleFactory {
    
      
    // 根据奶茶类型创建对应的奶茶对象  
    public static MilkTea createMilkTea(String type) {
    
      
        switch (type) {
    
      
            case "pearl":  
                return new PearlMilkTea();  
            case "matcha":  
                return new MatchaLatte();  
            case "strawberry":  
                return new StrawberryMilkTea();  
            default:  
                throw new IllegalArgumentException("未知的奶茶类型");  
        }  
    }  
}  
  
// 客户端测试类  
public class SimpleFactoryClient {
    
      
    public static void main(String[] args) {
    
      
        MilkTea tea1 = SimpleFactory.createMilkTea("pearl");  
        tea1.make(); // 输出:制作珍珠奶茶  
  
        MilkTea tea2 = SimpleFactory.createMilkTea("matcha");  
        tea2.make(); // 输出:制作抹茶拿铁  
  
        // 如果添加新口味的奶茶,比如芒果奶茶,需要修改SimpleFactory类  
    }  
}

如果这家奶茶店想要推出新口味的奶茶(比如芒果奶茶),店员就需要学习新口味的制作方法,并且还要在菜单上添加这个新选项。这就意味着店员(即工厂类)的职责范围需要扩大,因此其代码也需要相应地进行修改,以支持这种新口味的奶茶。

// 新增芒果奶茶类  
class MangoMilkTea implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作芒果奶茶");  
    }  
}  
  
// 简单工厂类,负责创建所有种类的奶茶(已修改以支持芒果奶茶)  
class SimpleFactory {
    
      
    // 根据奶茶类型创建对应的奶茶对象(已修改以支持芒果奶茶)  
    public static MilkTea createMilkTea(String type) {
    
      
        switch (type) {
    
      
            case "pearl":  
                return new PearlMilkTea();  
            case "matcha":  
                return new MatchaLatte();  
            case "strawberry":  
                return new StrawberryMilkTea();  
            case "mango": // 新增的芒果奶茶选项  
                return new MangoMilkTea();  
            default:  
                throw new IllegalArgumentException("未知的奶茶类型");  
        }  
    }  
}  
  
// 客户端测试类(可以添加测试芒果奶茶的代码)  
public class SimpleFactoryClient {
    
      
    public static void main(String[] args) {
    
      
        MilkTea tea1 = SimpleFactory.createMilkTea("pearl");  
        tea1.make(); // 输出:制作珍珠奶茶  
  
        MilkTea tea2 = SimpleFactory.createMilkTea("matcha");  
        tea2.make(); // 输出:制作抹茶拿铁  
  
        MilkTea tea3 = SimpleFactory.createMilkTea("strawberry");  
        tea3.make(); // 输出:制作草莓奶茶  
  
        // 测试新口味的芒果奶茶  
        MilkTea tea4 = SimpleFactory.createMilkTea("mango");  
        tea4.make(); // 输出:制作芒果奶茶  
    }  
}

如果 类存在@Autowired 的使用时,new 对象的操作就需要换成 ApplicationContext 去获取

// 珍珠奶茶类,现在是一个 Spring 组件  
@Component  
class PearlMilkTea implements MilkTea {
    
      
    @Autowired 
    private PearMapper pearMapper;  
   
    @Override  
    public void make() {
    
      
        // 使用 pearMapper 做一些事情(例如查询数据库)  
        pearMapper.select()
        // ...  
        System.out.println("制作珍珠奶茶");  
    }  
}  


class SimpleFactory implements ApplicationContextAware {
    
    

    ApplicationContext applicationContext;
    // 根据奶茶类型创建对应的奶茶对象  
    public static MilkTea createMilkTea(String type) {
    
    
        switch (type) {
    
    
            case "pearl":
                return applicationContext.getBean(PearlMilkTea.Class);
            case "matcha":
                return new MatchaLatte();
            case "strawberry":
                return new StrawberryMilkTea();
            default:
                throw new IllegalArgumentException("未知的奶茶类型");
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        this.applicationContext=applicationContext;
    }
}

二、工厂方法模式

工厂方法模式:定义了一个用于创建对象的接口,让子类决定实例化哪一个类。
工厂方法使一个类的实例化延迟到子类。

优点

  1. 单一职责:每个具体工厂类只负责创建一种产品,代码更加简洁。
  2. 扩展性强:完全满足开闭原则(OCP:对新增开放,对修改关闭),新增产品类时无需修改已有代码,只需新增具体产品类和对应工厂类。

缺点

  1. 增加开发量:每增加一个新产品,都需要新增一个具体产品类和一个对应的具体工厂类,增加了额外的开发工作量。
  2. 维护复杂:当需要修改多个产品类时,可能需要同时修改多个对应的工厂类,维护成本增加。

举例说明

您来到了一个奶茶一条街,这里有多家不同品牌的奶茶店,比如“A品牌奶茶店”和“B品牌奶茶店”, 奶茶店(品牌) 本身就相当于工厂。每家店都有自己的特色奶茶(比如珍珠奶茶、抹茶拿铁和草莓奶茶),并且每家店都有自己的店员(相当于具体工厂类)来负责制作这些奶茶。

您可以根据自己的喜好选择一家奶茶店,然后走进店里告诉店员您想喝的奶茶类型。
店员会根据自己店铺的特色为您制作一杯奶茶。
如果某家店想要推出新口味的奶茶(比如芒果奶茶),他们只需要在自己的店铺里添加这种新口味的奶茶,并培训店员掌握新的制作方法。
这样,其他店铺就不会受到影响,因为每家店都有自己的店员和独特的制作流程。

这种模式下,新增产品时只需要修改对应的具体工厂类,而无需修改工厂接口或现有的其他具体工厂类,从而提高了系统的扩展性和灵活性。

Java代码示例

// 奶茶接口  
interface MilkTea {
    
      
    void make();  
}  
  
// 珍珠奶茶类  
class PearlMilkTeaImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作珍珠奶茶");  
    }  
}  
  
// 抹茶拿铁类  
class MatchaLatteImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作抹茶拿铁");  
    }  
}  
  
// 草莓奶茶类  
class StrawberryMilkTeaImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作草莓奶茶");  
    }  
}  
  
// 工厂接口  
interface MilkTeaFactory {
    
      
    MilkTea createMilkTea();  
}  
  
// A品牌奶茶店工厂类  
class BrandAMilkTeaFactory implements MilkTeaFactory {
    
      
    @Override  
    public MilkTea createMilkTea() {
    
      
        return new PearlMilkTeaImpl(); // 假设A品牌只卖珍珠奶茶  
    }  
}  
  
// B品牌奶茶店工厂类  
class BrandBMilkTeaFactory implements MilkTeaFactory {
    
      
    @Override  
    public MilkTea createMilkTea() {
    
      
        return new MatchaLatteImpl(); // 假设B品牌只卖抹茶拿铁  
    }  
}  
  
// 客户端测试类  
public class FactoryMethodClient {
    
      
    public static void main(String[] args) {
    
      
        MilkTeaFactory factoryA = new BrandAMilkTeaFactory();  
        MilkTea teaA = factoryA.createMilkTea();  
        teaA.make(); // 输出:制作珍珠奶茶  
  
        MilkTeaFactory factoryB = new BrandBMilkTeaFactory();  
        MilkTea teaB = factoryB.createMilkTea();  
        teaB.make(); // 输出:制作抹茶拿铁  
  
        // 如果A品牌想要推出新口味的奶茶,
        // 比如芒果奶茶,只需要在BrandAMilkTeaFactory中添加新的实现  
    }  
}

如果A品牌奶茶店想要推出新口味的奶茶,比如芒果奶茶

// 奶茶接口  
interface MilkTea {
    
      
    void make();  
}  
  
// 珍珠奶茶类  
class PearlMilkTeaImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作珍珠奶茶");  
    }  
}  
  
// 抹茶拿铁类  
class MatchaLatteImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作抹茶拿铁");  
    }  
}  
  
// 草莓奶茶类  
class StrawberryMilkTeaImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作草莓奶茶");  
    }  
}  
  
// 新增芒果奶茶类  
class MangoMilkTeaImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作芒果奶茶");  
    }  
}  
  
// 奶茶类型枚举  
enum MilkTeaType {
    
      
    PEARL,  
    MATCHA_LATTE,  
    STRAWBERRY,  
    MANGO // 新增芒果奶茶类型  
}  
  
// 工厂接口(稍作修改以支持类型参数)  
interface MilkTeaFactory {
    
      
    MilkTea createMilkTea(MilkTeaType type); // 引入类型参数  
}  
  
// A品牌奶茶店工厂类(修改以支持多种奶茶)  
class BrandAMilkTeaFactory implements MilkTeaFactory {
    
      
    @Override  
    public MilkTea createMilkTea(MilkTeaType type) {
    
      
        switch (type) {
    
      
            case PEARL:  
                return new PearlMilkTeaImpl();  
            case MANGO: // 新增对芒果奶茶的支持  
                return new MangoMilkTeaImpl();  
            default:  
                throw new IllegalArgumentException("Unsupported MilkTeaType: " + type);  
        }  
    }  
}  
  
// B品牌奶茶店工厂类(保持不变,但也可以按类似方式扩展)  
class BrandBMilkTeaFactory implements MilkTeaFactory {
    
      
    @Override  
    public MilkTea createMilkTea(MilkTeaType type) {
    
      
        // 这里为了简单起见,我们只提供一种奶茶,但可以根据需要扩展  
        if (type == MilkTeaType.MATCHA_LATTE) {
    
      
            return new MatchaLatteImpl();  
        } else {
    
      
            throw new IllegalArgumentException("Unsupported MilkTeaType: " + type);  
        }  
    }  
}  
  
// 客户端测试类  
public class FactoryMethodClient {
    
      
    public static void main(String[] args) {
    
      
        MilkTeaFactory factoryA = new BrandAMilkTeaFactory();  
        MilkTea teaA1 = factoryA.createMilkTea(MilkTeaType.PEARL);  
        teaA1.make(); // 输出:制作珍珠奶茶  
  
        MilkTea teaA2 = factoryA.createMilkTea(MilkTeaType.MANGO); // 新增对芒果奶茶的创建  
        teaA2.make(); // 输出:制作芒果奶茶  
  
        MilkTeaFactory factoryB = new BrandBMilkTeaFactory();  
        MilkTea teaB = factoryB.createMilkTea(MilkTeaType.MATCHA_LATTE);  
        teaB.make(); // 输出:制作抹茶拿铁  
    }  
}

三、抽象工厂模式

定义了一个接口用于创建相关或依赖对象的家族,而无需明确指定具体类。
该模式是对工厂方法模式的进一步升级,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

优点:

  1. 高内聚低耦合:抽象工厂模式将产品的创建与使用分离,降低了客户端代码与产品类之间的耦合度,同时产品族内的产品以高内聚的形式存在,有助于代码的维护和管理。
  2. 扩展性好:完全满足开闭原则(OCP:对新增开放,对修改关闭),当需要添加新的产品族时,只需要添加一个新的具体工厂类和相应的产品类,而不需要修改原有的客户端代码。
  3. 灵活性高:通过抽象工厂模式设计,系统可以更加灵活地应对需求的变化,新增组件或平台时,只需新增新的抽象组件和具体平台工厂,无需修改已有代码。

缺点:

  1. 增加复杂性:抽象工厂模式增加了系统的抽象性和复杂性,可能导致系统难以理解和维护。
  2. 扩展新产品困难:如果只需要添加新的产品而不是新的产品族,那么抽象工厂模式可能会变得笨拙,因为需要修改所有的工厂接口和工厂实现类。

举例说明

您来到了一个更加高级的奶茶广场,这里不仅有奶茶店,还有卖杯子、吸管等配件的店铺。而且,这些店铺都是连锁经营的,比如“热带风味奶茶店”和“经典风味奶茶店”,它们不仅提供奶茶,还提供与奶茶风格相匹配的杯子和吸管等配件。

您可以选择自己喜欢的奶茶店品牌,然后走进这家店。在这里,您不仅可以喝到他们特色的奶茶,还可以买到与奶茶风格相匹配的杯子和吸管等配件。这些产品都是一系列相互关联的,共同构成了一个产品族。

如果某家店想要推出新口味的奶茶或者新风格的杯子和吸管(比如蓝莓奶茶或陶瓷杯子),他们只需要在自己的品牌内部进行更新和调整。这样,您就可以在不同的品牌之间自由选择,并且每次都能得到一整套风格一致的奶茶及其配件。

这种模式下,新增产品族时只需要在对应的具体工厂类中添加新的产品创建方法,而无需修改抽象工厂接口或现有的其他具体工厂类(除非新增的产品引入了新的产品等级)。这进一步提高了系统的扩展性和灵活性。

Java代码示例

// 奶茶接口  
interface MilkTea {
    
      
    void make();  
}  
  
// 杯子接口  
interface Cup {
    
      
    void show();  
}  
  
// 珍珠奶茶类  
class PearlMilkTeaImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作珍珠奶茶");  
    }  
}  
  
// 抹茶拿铁类  
class MatchaLatteImpl implements MilkTea {
    
      
    @Override  
    public void make() {
    
      
        System.out.println("制作抹茶拿铁");  
    }  
}  
  
// 塑料杯类  
class PlasticCupImpl implements Cup {
    
      
    @Override  
    public void show() {
    
      
        System.out.println("这是塑料杯");  
    }  
}  
  
// 陶瓷杯类  
class CeramicCupImpl implements Cup {
    
      
    @Override  
    public void show() {
    
      
        System.out.println("这是陶瓷杯");  
    }  
}  
  
// 抽象工厂接口  
interface AbstractFactory {
    
      
    MilkTea createMilkTea();  
    Cup createCup();  
}  
  
// 热带风味奶茶店工厂类  
class TropicalFlavorFactory implements AbstractFactory {
    
      
    @Override  
    public MilkTea createMilkTea() {
    
      
        //制作珍珠奶茶
        return new PearlMilkTeaImpl();  
    }  
  
    @Override  
    public Cup createCup() {
    
     
        //塑料杯
        return new PlasticCupImpl();  
    }  
}  
  
// 经典风味奶茶店工厂类  
class ClassicFlavorFactory implements AbstractFactory {
    
      
    @Override  
    public MilkTea createMilkTea() {
    
      
        //制作抹茶拿铁
        return new MatchaLatteImpl();  
    }  
  
    @Override  
    public Cup createCup() {
    
     
        //陶瓷杯
        return new CeramicCupImpl();  
    }  
}  
  
// 客户端测试类  
public class AbstractFactoryClient {
    
      
    public static void main(String[] args) {
    
      
        AbstractFactory factory1 = new TropicalFlavorFactory();  
        MilkTea tea1 = factory1.createMilkTea();  
        Cup cup1 = factory1.createCup();  
        tea1.make(); // 输出:制作珍珠奶茶  
        cup1.show(); // 输出:这是塑料杯  
  
        AbstractFactory factory2 = new ClassicFlavorFactory();  
        MilkTea tea2 = factory2.createMilkTea();  
        Cup cup2 = factory2.createCup();  
        tea2.make(); // 输出:制作抹茶拿铁  
        cup2.show(); // 输出:这是陶瓷杯  
  
        // 如果热带风味奶茶店想要推出新口味的奶茶或新风格的杯子,
        // 只需要在自己的工厂类中添加新的实现  
    }  
}

猜你喜欢

转载自blog.csdn.net/qq_20236937/article/details/143186445