创造型模式
AI 整理版
本篇已整理为更适合学习与复习的笔记结构,欢迎前往阅读:创造型模式-AI整理版。
创造型模式是设计模式中的一类,它主要用于处理对象的创建,隐藏对象的创建细节,提供更加灵活的对象创建方式,降低系统的耦合度。
常见的创造型模式包括:
- 简单工厂模式(Simple Factory Pattern)
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 原型模式(Prototype Pattern)
- 建造者模式(Builder Pattern)
其中,简单工厂模式、工厂方法模式和抽象工厂模式都是工厂模式的不同变体,它们都用于创建对象;
单例模式用于保证一个类只有一个实例;
原型模式用于复制对象;
建造者模式用于创建复杂对象。
简单工厂模式(Simple Factory Pattern)
简单工厂模式是一种创建型设计模式,它通过一个工厂类来创建不同类型的对象。简单工厂模式将对象的创建过程封装在一个工厂类中,客户端只需要通过工厂类来获取所需的对象,而不需要知道具体的对象创建过程。
在简单工厂模式中,工厂类负责创建不同类型的对象,并且可以根据客户端的需求来创建不同的对象。工厂类通常包含一个静态方法,该方法接收一个参数,根据参数的不同来创建不同类型的对象。
简单工厂模式的优点是可以将对象的创建过程封装起来,客户端不需要知道具体的对象创建过程,从而减少了客户端的耦合度。同时,简单工厂模式也可以提高代码的复用性,因为可以在不同的客户端中重复使用同一个工厂类。
简单工厂模式的缺点是当需要创建新类型的对象时,需要修改工厂类的代码,从而违反了开放-封闭原则。此外,简单工厂模式也会导致工厂类的代码变得过于复杂,难以维护。
如何使用
在Spring Boot中使用简单工厂模式,可以通过以下步骤实现:
定义一个动物接口Animal,并且实现接口:
java/** * @description: 动物接口 * @author: black tea * @date: 2023/3/18 23:30 */ public interface Animal { /** * 动物发出声音 */ void makeSound(); } /** * @description: * @author: black tea * @date: 2023/3/18 23:31 */ @Slf4j @Service public class Cat implements Animal { @Override public void makeSound() { log.info("喵喵喵"); } } /** * @description: * @author: black tea * @date: 2023/3/18 23:31 */ @Slf4j @Service public class Dog implements Animal{ @Override public void makeSound() { log.info("汪汪汪"); } } /** * @description: * @author: black tea * @date: 2023/3/19 0:35 */ @Slf4j @Service public class SeaOtter implements Animal { @Override public void makeSound() { log.info("咕噜咕噜"); } }创建一个工厂类AnimalFactory,并且通过注入Map形式的Animal接口实现来实现获取对应Animal实现类。
java/** * @description: * @author: black tea * @date: 2023/3/18 23:33 */ @Component public class AnimalFactory { @Resource Map<String, Animal> animalMap; public Optional<Animal> getAnimal(String animalType){ if (animalType == null){ return Optional.empty(); } // 将首字母变小写 String newAnimalType = StrUtil.lowerFirst(animalType); return Optional.ofNullable(animalMap.get(newAnimalType)); } }测试使用:
java@Autowired private AnimalFactory animalFactory; /** * 测试 简单工厂模式 */ @Test void testSimpleFactoryModel() { log.debug("测试 简单工厂模式"); Optional<Animal> catOpt = animalFactory.getAnimal(SeaOtter.class.getSimpleName()); catOpt.ifPresent(Animal::makeSound); }
GitHub学习地址:java-design-patterns/README.md at master · iluwatar/java-design-patterns (github.com)
工厂方法模式(Factory Method Pattern)
工厂方法模式是一种创建型设计模式,它定义了一个抽象的工厂接口,具体的工厂类实现该接口来创建不同类型的对象。工厂方法模式将对象的创建过程委托给具体的工厂类,从而将对象的创建和使用分离开来。
在工厂方法模式中,抽象工厂接口中通常包含一个工厂方法,该方法由具体的工厂类来实现,用于创建不同类型的对象。客户端通过调用工厂方法来获取所需的对象,而不需要知道具体的对象创建过程。
工厂方法模式的优点是可以将对象的创建过程和使用过程分离开来,从而提高代码的灵活性和可维护性。同时,工厂方法模式也符合开放-封闭原则,因为可以通过添加新的工厂类来扩展系统的功能。
工厂方法模式的缺点是需要定义大量的工厂类,从而增加了代码的复杂度。此外,工厂方法模式也可能导致代码的重复,因为不同的工厂类可能会创建相同类型的对象。
总的来说,工厂方法模式适用于需要创建一系列相关的对象,但是具体的对象类型在运行时才能确定的情况下。
如何使用
在Spring Boot中使用工厂方法模式,可以通过以下步骤实现:
定义一个接口UserService,并且实现接口UserServiceImpl:
java/** * @description: * @author: black tea * @date: 2023/3/19 1:08 */ public interface UserService { void addUser(Map<String, Object> params); } /** * @description: * @author: black tea * @date: 2023/3/19 1:10 */ @Slf4j @Import(SpringUtil.class) // 如果需要在这里调用Spring管理的Bean,需要导入 ApplicationContext public class UserServiceImpl implements UserService { @Override public void addUser(Map<String, Object> params) { log.info("添加用户,参数是:{}", JSONUtil.toJsonStr(params)); Optional.ofNullable((Cat) SpringUtil.getBean("cat")) .ifPresent(Cat::makeSound); } }创建工厂类UserServiceFactory并且实现FactoryBean接口,重写 getObject方法返回一个UserServiceImpl对象:
java/** * @description: * @author: black tea * @date: 2023/3/19 1:12 */ @Component public class UserServiceFactory implements FactoryBean<UserService> { @Override public UserService getObject() throws Exception { return new UserServiceImpl(); } @Override public Class<?> getObjectType() { return UserService.class; } @Override public boolean isSingleton() { return true; } }单元测试使用:
java@Autowired private UserService userService; /** * 测试 工厂方法模式 */ @Test public void testFactorymethod() throws Exception { log.debug("测试 工厂方法模式"); Map<String, Object> userMap = new HashMap<>(); userMap.put("username", "张三"); userMap.put("age", 28); userService.addUser(userMap); }
GitHub学习地址:java-design-patterns/README.md at master · iluwatar/java-design-patterns (github.com)
抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式是一种创建型设计模式,它定义了一个抽象的工厂接口,具体的工厂类实现该接口来创建一组相关的对象。抽象工厂模式将对象的创建过程封装在一个工厂类中,客户端只需要通过工厂类来获取所需的对象,而不需要知道具体的对象创建过程。
在抽象工厂模式中,抽象工厂接口中通常包含多个工厂方法,每个工厂方法用于创建一个相关的对象。具体的工厂类实现这些工厂方法来创建一组相关的对象。客户端通过调用工厂方法来获取所需的对象,而不需要知道具体的对象创建过程。
抽象工厂模式的优点是可以将对象的创建过程封装起来,客户端不需要知道具体的对象创建过程,从而减少了客户端的耦合度。同时,抽象工厂模式也可以提高代码的复用性,因为可以在不同的客户端中重复使用同一个工厂类。
抽象工厂模式的缺点是当需要创建新类型的对象时,需要修改抽象工厂接口和所有的具体工厂类的代码,从而违反了开放-封闭原则。此外,抽象工厂模式也会导致工厂类的代码变得过于复杂,难以维护。
总的来说,抽象工厂模式适用于需要创建一组相关的对象,且这些对象的类型在运行时才能确定的情况下。
如何使用
在Spring Boot中使用抽象工厂模式,可以通过以下步骤实现:
这里的代码案例场景有支付方式和小程序类型,我如下只选择了支付方式了说明,因为两个其实包含的理念一样。
这里我使用一个支付的业务场景来说明,不过这里做了不同支付方式的案例。
定义一个支付接口Pay,并且实现该接口:
java/** * @description: 支付接口 * @author: black tea * @date: 2023/3/19 14:40 */ public interface Pay { void pay(Map<String, Object> payParams); } /** * @description: * @author: black tea * @date: 2023/3/19 14:42 */ @Slf4j public class AliPayService implements Pay { @Override public void pay(Map<String, Object> payParams) { log.info("支付宝支付"); } } /** * @description: * @author: black tea * @date: 2023/3/19 14:41 */ @Slf4j public class WeChatPayService implements Pay { @Override public void pay(Map<String, Object> payParams) { log.info("微信支付"); } }定义一个支付方式工厂接口AbstractPayFactory,里面存在两个方法,分别返回不同支付方式的对象。
java/** * @description: * @author: black tea * @date: 2023/3/19 14:44 */ public interface AbstractPayFactory { Optional<AliPayService> createAliPay(); Optional<WeChatPayService> createWxChatPay(); }创建一个工厂实现类ConcretePayFactory来实现AbstractPayFactory。
java/** * @description: * @author: black tea * @date: 2023/3/19 14:46 */ @Service public class ConcretePayFactory implements AbstractPayFactory { @Override public Optional<AliPayService> createAliPay() { return Optional.of(new AliPayService()); } @Override public Optional<WeChatPayService> createWxChatPay() { return Optional.of(new WeChatPayService()); } }单元测试使用:
java@Autowired private AbstractPayFactory payFactory; /** * 测试 抽象工厂方法 */ @Test void testAbstractPayFactory() { log.debug("测试 抽象工厂方法"); Map<String, Object> payMap = new HashMap<>(); payMap.put("user", "张三"); payMap.put("amount", new BigDecimal("22.5")); // 测试 支付 payFactory.createAliPay() .ifPresent(pay -> { payMap.put("payMethod", "支付宝"); pay.pay(payMap); }); payFactory.createWxChatPay() .ifPresent(pay -> { payMap.put("payMethod", "支付宝"); pay.pay(payMap); }); }
GitHub学习地址:java-design-patterns/README.md at master · iluwatar/java-design-patterns (github.com)
单例模式(Singleton Pattern)
单例模式是一种创建型设计模式,它的目的是确保在整个应用程序中只有一个类的实例,并提供全局访问点。实现单例模式的类只有一个实例,并提供访问该实例的全局方法。
单例模式的优点包括:
- 保证只有一个实例存在,减少了内存开销和系统资源的浪费。
- 提供了全局访问点,方便了代码的调用和管理。
但是单例模式也有一些缺点,比如:
- 单例类的职责不能过重,否则会导致代码的复杂性和可维护性下降。
- 单例类的扩展性不高,如果需要添加新的功能,可能需要修改单例类的代码,这可能会导致系统的不稳定。
总之,单例模式是一种非常常用的设计模式,它可以在需要的时候方便地创建唯一的实例,并提供全局访问点,从而提高代码的可维护性和可读性。
如何使用
在Spring Boot中使用单例模式,可以通过以下步骤实现:
这里写了三种单例,分别是:
Spring指定为单例,但是存在线程安全问题。
java/** * @description: 非线程安全 * @author: black tea * @date: 2023/3/19 16:15 */ @Component @Scope("singleton") // 指定类型为单例,实际上默认就是单例 public class MySingleton { }双重锁单例,用于解决线程安全问题。
java/** * @description: 双重检查锁来确保线程安全,但是这样会被反射获得 * @author: black tea * @date: 2023/3/19 16:15 */ @Component @Singleton // 用于标记一个类为单例模式。它的作用是告诉Spring框架,在整个应用程序中只需要创建一个该类的实例。 public class MySingleton2 { private static volatile MySingleton2 instance; private MySingleton2() {} public static MySingleton2 getInstance() { if (instance == null) { synchronized (MySingleton2.class) { if (instance == null) { instance = new MySingleton2(); } } } return instance; } }内部静态类,用于解决线程安全问题。
java/** * @description: 静态内部类实现 * @author: black tea * @date: 2023/3/19 16:40 * * 在这个示例中,使用@Component注解标记了MySingleton类,以便Spring框架能够将该类识别为一个可注入的bean。 * 静态内部类SingletonHolder包含了一个静态实例变量INSTANCE,该变量在类加载时被初始化,且只会被初始化一次。由于静态内部类的加载是线程安全的,因此可以保证在多线程环境下,只有一个实例被创建。 * 在getInstance()方法中,直接返回SingletonHolder.INSTANCE变量,这样就可以获取到该类的实例。 */ @Component public class MySingleton3 { private MySingleton3() {} private static class SingletonHolder { private static final MySingleton3 INSTANCE = new MySingleton3(); } public static MySingleton3 getInstance() { return SingletonHolder.INSTANCE; } }测试使用:
java@Autowired private MySingleton mySingleton1; @Autowired private MySingleton mySingleton2; @Autowired private MySingleton2 mySingleton21; @Autowired private MySingleton2 mySingleton22; @Autowired private MySingleton3 mySingleton31; @Autowired private MySingleton3 mySingleton32; /** * 测试 单例模式 */ @Test void testSingleton(){ log.info("测试 单例模式"); // 线程不安全之单例模式 log.info("线程不安全之单例模式{}", mySingleton1==mySingleton2?"成功":"失败"); /** * 双重检查锁之单例模式 * * 两种使用方式 * 1. MySingleton2.getInstance() * * 2. @Autowired * private MySingleton2 mySingleton22; * * 注意: * 1. 第一种方法不被Spring所管理,无法访问其他bean。 * 2. 虽然该类是单例,但并不代表两种方式的到的对象时同一个哦。 */ MySingleton2 instance1 = MySingleton2.getInstance(); MySingleton2 instance2 = MySingleton2.getInstance(); log.info("双重检查锁之单例模式{}", instance1==instance2?"成功":"失败"); log.info("双重检查锁之单例模式{}", mySingleton21==mySingleton22?"成功":"失败"); log.warn("双重检查锁之单例模式,mySingleton21 == instance1的结果为:{},由此可见,方式1和方式2的到的对象可不是同一个哦。", mySingleton21==instance1); /** * 内部静态类之单例模式 * 1. MySingleton3.getInstance() * * 2. @Autowired * private MySingleton3 mySingleton31; * * 注意: * 1. 第一种方法不被Spring所管理,无法访问其他bean。 * 2. 虽然该类是单例,但并不代表两种方式的到的对象时同一个哦。 */ log.info("内部静态类之单例模式{}", mySingleton31==mySingleton32?"成功":"失败"); MySingleton3 instance11 = MySingleton3.getInstance(); MySingleton3 instance22 = MySingleton3.getInstance(); log.info("内部静态类之单例模式{}", instance11==instance22?"成功":"失败"); log.warn("内部静态类之单例模式,mySingleton31 == instance21的结果为:{},由此可见,方式1和方式2的到的对象可不是同一个哦。", mySingleton31==instance11); }
GitHub学习地址:Java-design-patterns/singleton at master ·iluwatar/java-design-patterns (github.com)
原型模式(Prototype Pattern)
原型模式是一种创建型设计模式,其核心思想是通过复制已有对象来创建新的对象,而不是通过实例化类来创建新的对象。原型模式是一种基于对象的复制技术,它可以在不知道对象具体实现的情况下创建新的对象,同时可以避免创建大量的重复对象,提高系统的性能和效率。
在原型模式中,原型类是一个可被复制的对象,它定义了一个克隆方法,用于复制自身并返回一个新的对象实例。克隆方法通常是通过实现Java中的Cloneable接口来实现的。
使用原型模式的主要优点是可以减少对象的创建次数,提高系统的性能和效率,同时也可以避免对象的重复创建。原型模式的主要缺点是需要实现Cloneable接口,并重写clone方法,这对于一些复杂的对象来说可能会比较困难。
总之,原型模式是一种非常实用的设计模式,它可以在需要创建大量对象的情况下,提高系统的性能和效率,同时也可以避免对象的重复创建。
如何使用
在Spring Boot中使用原型模式,可以通过以下步骤实现:
定义一个原型类Prototype实现Cloneable接口:
java/** * @description: * @author: black tea * @date: 2023/3/19 19:04 */ public class Prototype implements Cloneable { private String name; public Prototype(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Prototype clone() throws CloneNotSupportedException { return (Prototype) super.clone(); } }创建一个配置类ProtoTypeConfig,用来定义Prototype类:
java/** * @description: * @author: black tea * @date: 2023/3/19 19:08 */ @Configuration public class ProtoTypeConfig { @Bean @Scope("prototype") public Prototype prototype(){ return new Prototype("prototype"); } }单元测试使用:
java@Autowired private BeanFactory beanFactory; @Autowired private Prototype prototype11; @Autowired private Prototype prototype22; /** * 测试 原型模式 * 结果: 原型返回的是新的对象,使用的是深克隆拷贝哦。 */ @Test void testPrototype() throws CloneNotSupportedException { log.info("测试 原型模式"); /** * 通过 beanFactory 获取 */ Prototype prototype1 = beanFactory.getBean(Prototype.class); Prototype prototype2 = prototype1.clone(); log.info(prototype1.getName()); // 输出 "prototype" log.info(prototype2.getName()); // 输出 "prototype" prototype2.setName("newPrototype"); log.info(prototype1.getName()); // 输出 "prototype" log.info(prototype2.getName()); // 输出 "newPrototype" /** * 通过 @Autowired 获取 */ log.info("prototype11==prototype22,结果为:{}", prototype11==prototype22); // prototype1==prototype2,结果为:false }
GitHub学习地址:Java 设计模式/原型在大师 ·iluwatar/java-design-patterns (github.com)
建造者模式(Builder Pattern)
建造者模式是一种创建型设计模式,它允许您创建复杂的对象,同时将其构造过程与表示分离。它涉及将构造逻辑封装在一个单独的对象中,这使得您可以改变一个对象的内部表示方式,而不会影响到客户端代码。
在建造者模式中,您将一个复杂对象的构造过程分解成多个简单的步骤,并将每个步骤的实现委托给一个单独的建造者对象。客户端代码只需指定要构建的对象类型和所需的步骤,然后将它们传递给相应的建造者对象,以便构建出所需的对象。
建造者模式的优点包括:
- 可以隐藏对象的复杂构造过程,使客户端代码更简洁。
- 可以改变对象的内部表示方式,而不会影响到客户端代码。
- 可以将同一构造过程用于不同的对象,提高代码的复用性。
- 可以将构造逻辑与表示分离,使得代码更易于维护。
建造者模式的缺点包括:
- 建造者对象的数量可能会增加,增加代码的复杂性。
- 构造过程的顺序可能会影响最终对象的表示方式。
- 对象的构造过程可能会变得更加复杂,需要更多的代码来实现。
总之,建造者模式是一种有效的创建复杂对象的方法,它可以将构造逻辑与表示分离,提高代码的复用性和可维护性。
如何使用
在Spring Boot中使用建造者模式,可以通过以下步骤实现:
将一个User对象(建造者)转化为UserEntity对象的场景。其实如果单讲如何创建建造者,User类已经可以实现了,但是使用时会存在一些转化的问题,例如:User user = mapper.convertValue(userEntity, User.class);
转化时会报错,因为User里缺少了UserEntity对象里的一些属性,所以我这里考虑之下将我如何使用的方式进行编写。
定义一个建造者User类,一个UserEntity类,后者类属性比前者多:
java/** * @description: * @author: black tea * @date: 2023/3/19 19:36 */ @Data @NoArgsConstructor @AllArgsConstructor @Builder // 实现建造者 public class User { private int id; private String username; private String email; } /** * @description: * @author: black tea * @date: 2023/3/19 19:36 */ @Data @AllArgsConstructor @NoArgsConstructor public class UserEntity{ private int id; private String username; private String email; private Map<String, Object> other; }因为我使用的mapstruct(以下简称ms),所以需要在pom里面加入两个依赖:
xml<dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> </dependency>导入依赖后,创建一个接口UserMapper:
java/** * @description: * @author: black tea * @date: 2023/3/19 20:17 */ @Mapper(componentModel = "spring") public interface UserMapper { UserEntity toEntity(User user); User toUser(UserEntity entity); }定义一个接口UserService并实现:
java/** * @description: * @author: black tea * @date: 2023/3/19 19:38 */ public interface UserService { User getUserById(int id) throws JsonProcessingException; } /** * @description: * @author: black tea * @date: 2023/3/19 19:38 */ @Service public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Override public User getUserById(int id) throws JsonProcessingException { // 假设从数据库中去到userEntity UserEntity userEntity = this.daoGetUserById(id); // 1. 将普通对象里的属性一个一个设置到构造者对象中 User build = User.builder() .id(id) .username(userEntity.getUsername()) .email(userEntity.getEmail()) .build(); // 2. 使用 MapStruct(推荐) User toUser = userMapper.toUser(userEntity); UserEntity toEntity = userMapper.toEntity(build); // 3. 使用jackson ObjectMapper mapper = new ObjectMapper(); // 将构造者对象转为普通对象 UserEntity newUserEntity = mapper.readValue( mapper.writeValueAsString(build), UserEntity.class ); // 存在缺少的对象时会报错,例如现在缺少other,这种就需要自定义序列化 // User user = mapper.convertValue(userEntity, User.class); // 4. BeanUtils.copyProperties() User user = User.builder().build(); BeanUtils.copyProperties(userEntity, user); return build; } private UserEntity daoGetUserById(int id){ UserEntity userEntity = new UserEntity(); userEntity.setId(id); userEntity.setUsername("张三"); userEntity.setEmail("xxx@xx.com"); Map<String, Object> other = new HashMap<>(); other.put("icon", "https://icon.png"); userEntity.setOther(other); return userEntity; } }单元测试使用:
java@Autowired private com.blacktea.creationalpatterns.builderpattern.servce.UserService userService1; /** * 测试 建造者模式 * @throws JsonProcessingException */ @Test void testBuilderPattern() throws JsonProcessingException { log.info("测试 建造者模式"); User user = userService1.getUserById(1); log.info("打印user信息:{}", user.toString()); }注意:ms是根据类型、属性名称之间进行自动转换的,如下就是本次案例转换后生成的类:
java@Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2023-03-23T18:44:41+0800", comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_271 (Oracle Corporation)" ) @Component public class UserMapperImpl implements UserMapper { @Override public UserEntity toEntity(User user) { if ( user == null ) { return null; } UserEntity userEntity = new UserEntity(); userEntity.setId( user.getId() ); userEntity.setUsername( user.getUsername() ); userEntity.setEmail( user.getEmail() ); return userEntity; } @Override public User toUser(UserEntity entity) { if ( entity == null ) { return null; } UserBuilder user = User.builder(); user.id( entity.getId() ); user.username( entity.getUsername() ); user.email( entity.getEmail() ); return user.build(); } }
GitHub学习地址:java-design-patterns/localization/zh/builder at master · iluwatar/java-design-patterns (github.com)
