设计模式③ :生成实例

news2024/12/23 17:48:06

文章目录

  • 一、前言
  • 二、Singleton 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 三、Prototype 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 四、Builder 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 五、Abstract Factory 模式
    • 1. 介绍
    • 2. 应用
    • 3. 总结
  • 参考内容

一、前言

有时候不想动脑子,就懒得看源码又不像浪费时间所以会看看书,但是又记不住,所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》(【日】结城浩 著)。该系列文章可随意转载。

二、Singleton 模式

Singleton 模式 :只有一个实例

1. 介绍

何为Singleton 模式 :

  • 想确保任何情况下都绝对只有一个实例
  • 想在程序上表现出“只存在一个实例”

像这种确保只生成一个实例的模式被称为Singleton 模式。


Singleton 模式登场的角色:

  • Singleton :在 Singleton 模式中只有这一个角色。Singleton 角色中有一个返回唯一实例的 static 方法,该方法总是会返回同一个实例。

类图如下:
在这里插入图片描述


Demo 如下:

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
            	if (instance == null) {
                	instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2. 应用

  • Spring 容器中 bean 默认的就是单例的。实际上是每个 Bean在创建后都会注册到一个 Map集合中。但实际上,Spring中的单例模式完成了保证一个类仅有一个实例,但没有从构造器级别去控制单例,这是因为spring管理的是任意的java对象。


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 目前的单例模式除了自定义一些工具类或者资源类之外,大部分情况下都是通过将 Bean 实例注入到Spring容器中,依赖于Spring的特性来保证实例的唯一性 。

3. 总结

单例模式有多种实现 饱汉式、饿汉式、DCL等等,这里不做过多赘述,如有需要,可以参考:设计模式-单例模式介绍和使用场景及Spring单例模式的使用


相关设计模式:以下设计模式中,多数情况下都只会生成一个实例。

  • AbstractFactory模式
  • Builder 模式
  • Facade 模式
  • Prototype 模式

三、Prototype 模式

Prototype 模式 :通过复制生成实例

1. 介绍

在开发过程中会存在 在不指定类名的前提下生成实例 的需求,如:

  1. 对象种类繁多,无法将它们整合到一个类中 :该种情况是需要处理的对象太多,如果将它们分别作为一个类,必须要编写很多个类文件。
  2. 难以根据类生成实例时 :该种情况是生成实例的过程太过复杂,很难根据类来生成实例。
  3. 想解耦框架与生成的实例时 :该种情况是想让生成实例的框架不依赖具体的类。这时,不能指定类名来生成实例,而要实现“注册”一个“原型”实例,然后通过复制该实例来生成新的实例。

Prototype 模式中登场的角色

  • Prototype(原型):该角色负责定义用于复制现有实例来生成新实例的方法。在下面Demo中由 Product 接口扮演该角色
  • ConcretePrototype(具体的原型):该角色负责实现复制现有实例并生成新的实例方法。在下面Demo中由 MessageBox 和 UnderlinePen 接口扮演该角色
  • Client(使用者):该角色负责使用复制实例的方法生成新的实例。在下面Demo中由 Manger 接口扮演该角色

类图如下:
在这里插入图片描述


Demo 如下:

  • Manager : 调用 createClone 方法复制实例的类
  • Product :声明了抽象方法 use 和 createClone 的接口
  • MessageBox :将字符串放入方框中并使其显示出来的类。Product 的子类
  • UnderlinePen :给字符串加上下划线并使其显示出来的类。Product 的子类

public class Manager {
    private Map<String, Product> registeredProducts = Maps.newHashMap();

    /**
     * 注册 Product
     * @param name
     * @param product
     */
    public void register(String name, Product product) {
        registeredProducts.put(name, product);
    }

    /**
     * 创建 Product
     * @param productName
     * @return
     */
    public Product create(String productName) {
        return registeredProducts.get(productName).createClone();
    }
}

// Product 接口
public interface Product extends Cloneable {
    /**
     * 使用该 Product
     * @param msg
     */
    void user(String msg);

    /**
     * 通过 clone 创建出一个 Product实例
     * @return
     */
    Product createClone();
}


public class MessageBox implements Product {
    private char msgChar;

    public MessageBox(char msgChar) {
        this.msgChar = msgChar;
    }

    @Override
    public void user(String msg) {
        for (int i = 0; i < msg.length() + 4; i++) {
            System.out.print(msgChar);
        }
        System.out.println();
        System.out.println(msgChar + " " + msg + " " + msgChar);
        for (int i = 0; i < msg.length() + 4; i++) {
            System.out.print(msgChar);
        }
        System.out.println();
    }

    @Override
    public Product createClone() {
        try {
            return (Product) clone();
        } catch (Exception e) {
            return null;
        }
    }
}


public class UnderlinePen implements Product {

    private char msgChar;

    public UnderlinePen(char msgChar) {
        this.msgChar = msgChar;
    }

    @Override
    public void user(String msg) {
        System.out.println(msg);
        for (int i = 0; i < msg.length(); i++) {
            System.out.print(msgChar);
        }
        System.out.println();
    }

    @Override
    public Product createClone() {
        try {
            return (Product) clone();
        } catch (Exception e) {
            return null;
        }
    }
}

// 测试用main 方法
public class DemoMain {
    public static void main(String[] args) {
        Manager manager = new Manager();
        UnderlinePen underlinePen = new UnderlinePen('~');
        MessageBox messageBox = new MessageBox('*');

        manager.register("underlinePen", underlinePen);
        manager.register("messageBox", messageBox);

        manager.create("underlinePen").user("Hello World");

        System.out.println();

        manager.create("messageBox").user("Hello World");

    }
}

Main 方法执行后输出如下

Hello World
~~~~~~~~~~~

***************
* Hello World *
***************

2. 应用

  • 如利用 org.springframework.beans.BeanUtils#copyProperties 方法对 Bean 进行拷贝复制时则是通过已有 Bean 拷贝复制出新的 Bean。


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 在某项目中,当遇到异常情况时需要发送告警信息,告警信息多种多样,但是标题信息不同,这时使用 Prototype 模式来 拷贝告警信息的基础信息。(不过实际上即是不依靠 Prototype 模式也可以实现该需求,不过这里通过 WarnMsgManager 的存在使其与 WarnMsg 解耦)如下:

    // 告警消息基础类
    @Slf4j
    @Data
    public class WarnMsg implements Cloneable {
        /**
         * 消息标题
         */
        private String title;
        /**
         * 消息内容
         */
        private String content;
        /**
         * 环境
         */
        private String env;
    
        /**
         * 需要的话可以重写该方法
         *
         * @return
         */
        public WarnMsg clone(String content) {
            try {
                final WarnMsg clone = (WarnMsg) clone();
                clone.setContent(content);
                return clone;
            } catch (Exception e) {
                log.warn("[告警信息][克隆失败]", e);
            }
            return null;
        }
    
    }
    
    // 上线告警
    public class OnlineWarnMsg extends WarnMsg {
        public OnlineWarnMsg() {
            setTitle("上线");
            setEnv("AAA");
        }
    }
    
    // 离线告警
    public class OfflineWarnMsg extends WarnMsg {
        /**
         * 设置消息的初始属性,假设这里有很多属性
         */
        public OfflineWarnMsg() {
            setTitle("下线");
            setEnv("AAA");
        }
    }
    
    // 告警消息管理类
    public class WarnMsgManager {
        /**
         * 通过 warnMsgMap 可以将 WarnMsg 与 WarnMsgManager 解耦
         */
        private Map<String, WarnMsg> warnMsgMap = Maps.newHashMap();
    
        /**
         * 注册消息类型
         * @param name
         * @param warnMsg
         */
        public void register(String name, WarnMsg warnMsg) {
            warnMsgMap.put(name, warnMsg);
        }
    
        /**
         * 创建对应消息类型
         * @param name
         * @return
         */
        public WarnMsg create(String name, String content) {
            return warnMsgMap.get(name).clone(content);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            // 初始化数据
            WarnMsgManager warnMsgManager = new WarnMsgManager();
            warnMsgManager.register("offline", new OfflineWarnMsg());
            warnMsgManager.register("online", new OnlineWarnMsg());
            // 创建告警消息实例
            final WarnMsg online = warnMsgManager.create("online", "上线信息");
            final WarnMsg offline = warnMsgManager.create("online", "下线信息");
    
            System.out.println("offline = " + offline);
            System.out.println("online = " + online);
        }
    }
    

3. 总结

Prototype 模式可以用于 对象的构造成本比较高或是较为麻烦时,其是对象构建时需要频繁获取链接或锁的情况时,通过 clone 的效率会更高,否则可以使用new的形式。


相关设计模式

  • Flyweight 模式:使用 Prototype 模式可以生成一个与当前实例的状态完全相同的实例。而使用 Flyweight 模式可以在不同的地方使用同一个实例。
  • Memento 模式:使用 Prototype 模式可以生成一个与当前实例的状态完全相同的实例。而使用 Memento 模式可以保存当前实例的状态,实现快照和撤销功能。
  • Composite 模式以及 Decoratort 模式 :经常使用Composite 模式以及 Decoratort 模式 时,需要能够动态地创建复杂结构实例。这时可以使用 Prototype 模式,以方便生成实例。
  • Command 模式:想要复制 Command 模式中出现的命令时,可以使用 Prototype 模式。

扩展思路

  • 在 Java 对象中,我们可以通过 clone 方法来复制实例。但需要注意,如果需要调用 clone 方法,则对象需要实现 java.lang.Cloneable 接口(被复制对象的父类或父接口实现都可以)。另外 clone 方法的定义是在 java.lang.Object 中,并非在 java.lang.Cloneable 接口中, java.lang.Cloneable 接口仅仅用来标记 “可以使用 clone 方法”。
  • 实现了 java.lang.Cloneable 接口 调用 clone 方法进行复制时, clone 方法的返回值是复制出的新的实例(clone 方法内部所进行的处理是分配与要复制的实例同样大小的内存空间,接着将要复制的实例中的字段的值复制到所分配的内存空间中)
  • clone 方法所进行的只是浅拷贝,如果需要实现深拷贝则需要调用者自身重写。需要注意是 clone 在复制对象的时候并不会调用对象的构造函数,因此如果对象在构造函数中有一些特殊处理,则需要调用者自身去处理。

四、Builder 模式

Builder 模式 :组装复杂的实例

1. 介绍

Builder 模式中登场的角色

  • Builder (建造者):该角色负责定义用于生成实例的接口 ,其中定义了准备用于生成实例的方法。
  • ConcreteBuilder (具体的建造者):该角色负责实现 Builder 角色的接口的类,这里定义了在生成实例时实际被调用的方法,此外,该角色还定义了获取最终生成结果的方法。
  • Director(监工):该角色负责使用Builder 角色的接口来生成实例。它并不依赖于 ConcreteBuilder 角色。他只会调用在Builder 角色中定义的方法。
  • Client (使用者):该角色使用了Builder 模式。在示例程序中由 Main 类扮演该角色。

类图如下:在这里插入图片描述


Demo 如下:

// 抽象构建器类
public abstract class Builder {
    /**
     * 构建 title
     * @param title
     */
    public abstract void builderTitle(String title);

    /**
     * 构建 content
     * @param content
     */
    public abstract void builderContent(String content);

    /**
     * 构建结束 :可以做最后的整合或者一些必填参数的校验
     */
    public abstract void close();
}



// 使用 Builder 生成实例
public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    /**
     * 定义生成过程,并且可以做一些额外的操作
     */
    public void construct() {
        builder.builderTitle("title");
        builder.builderContent("content");
        builder.close();
    }
}


public class ConcreteBuilder extends Builder {
    private String fileContent = "";


    @Override
    public void builderTitle(String title) {
        fileContent += title + "\n";
    }

    @Override
    public void builderContent(String content) {
        fileContent += content;
    }

    @Override
    public void close() {
        // TODO : 可以用于校验一些必须填充的条件,或其他处理
    }


    public String getResult() {
        return fileContent;
    }
}

public class Main {
    public static void main(String[] args) {
        // 实际使用中会可能会由多种 Builder
        ConcreteBuilder concreteBuilder = new ConcreteBuilder();
        final Director director = new Director(concreteBuilder);
        director.construct();
        final String result = concreteBuilder.getResult();
        System.out.println(result);
    }
}

2. 应用

  • Spring Security 中的 HttpSecurity 的构建个人认为就是对复杂对象构建的应用,如下:
     @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 自定义登陆拦截器
            JwtLoginFilter jwtLoginFilter = new JwtLoginFilter();
            jwtLoginFilter.setAuthenticationManager(authenticationManagerBean());
            jwtLoginFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
            jwtLoginFilter.setAuthenticationFailureHandler(authenticationFailureHandler);
    
            JwtTokenFilter jwtTokenFilter = new JwtTokenFilter();
    
            // 使用自定义验证实现器
            JwtAuthenticationProvider jwtAuthenticationProvider = new JwtAuthenticationProvider(userDetailsService, passwordEncoder);
    
            // 登陆验证信息
            http.authenticationProvider(jwtAuthenticationProvider)
                    .authorizeRequests()
                    .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                        @Override
                        public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                            object.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
                            object.setAccessDecisionManager(accessDecisionManager);
                            return object;
                        }
                    })
                    .anyRequest().authenticated()
                    .and()
                    .formLogin();
    
            // jwt 拦截器配置
            http.sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //禁用session
                    .and()
                    .csrf().disable()
                    .addFilterAt(jwtLoginFilter, UsernamePasswordAuthenticationFilter.class) // 添加拦截器
                    .addFilterAfter(jwtTokenFilter, JwtLoginFilter.class);
    
            // 权限处理信息
            http.exceptionHandling()
                    //   用来解决认证过的用户访问无权限资源时的异常
                    .accessDeniedHandler(accessDeniedHandler)
                    // 用来解决匿名用户访问无权限资源时的异常
                    .authenticationEntryPoint(authenticationEntryPoint);
    
    
        }
    


个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 在某项目中,某一个接口返回的数据结果集相同,但是其内部会根据 Region 的不同通过不同的途径去获取数据,最终构建出来一个完成的数据集,如果对象的构建过程非常耗时可以将对象的部分内容异步构建。这里可以使用Builder。如下:

    // 顶层抽象类,protected  防止子类滥用方法
    public abstract class Builder {
        protected abstract String buildTitle(String msg);
    
        protected abstract String buildAuthor(String msg);
    
        protected abstract String buildContent(String msg);
    
        protected abstract boolean close();
    }
    // 增加获取数据集的方法
    public abstract class DataBuilder extends Builder {
        /**
         * 构建并校验
         * @return
         */
        public String getResult() {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(buildTitle("")).append("\n");
            stringBuilder.append(buildAuthor("")).append("\n");
            stringBuilder.append(buildContent("")).append("\n");
            close();
            return stringBuilder.toString();
        }
    }
    
    // 一个数据来源的实现,简单实现
    public class OneDataBuilder extends DataBuilder {
        @Override
        public String buildTitle(String msg) {
            return "title";
        }
    
        @Override
        public String buildAuthor(String msg) {
            return "author";
        }
    
        @Override
        public String buildContent(String msg) {
            return "content";
        }
    
        @Override
        public boolean close() {
            // TODO : 检验数据或对数据做最后的处理
            return true;
        }
    }
    
    // main 方法调用
    public class Main {
        public static void main(String[] args) {
            // 实际应用会有多个 DataBuilder。
            DataBuilder dataBuilder = new OneDataBuilder();
            // 获取结果。不同的Region数据来源不同,最后拼接的数据集 result。
            // 在某些 Region 中,DataBuilder 不同部分的数据甚至可以可以开启异步来获取。最终在 getResult 方法中拼接
            final String result = dataBuilder.getResult();
            System.out.println("result = " + result);
        }
    }
    
    

3. 总结

  • 在示例程序中, Main 并不知道有没有调用 Builder ,他仅仅是调用了 Director 的 construct 方法,这样 Director 就会开始工作完成了文档的编写,而 Main 对此一无所知。而对于 Director 知道 Builder ,并调用Builder 的方法来完成文档编写。但是他并不知道具体是 Builder 的哪个子类,他仅仅知道这是个 Builder 类。这样就可以完成类子类的传递,无论是哪个 Builder 的子类都可以借用 Director 来完成方法调用。
  • Builder 模式 可以用于复杂对象的创建,尤其是多个实现的复杂对象,在设想中,设置可以将部分耗时以及与主体数据无太多关联的数据异步构建,最后通过 close 或者其他方法来等待所有同步和异步数据构建完成,提高对象的创建效率。

相关设计模式 :

  • Template Method 模式:在 Builder 模式中,Director 角色控制 Builder 角色。在 Template Method 模式中,父类控制子类。
  • Composite 模式:有些情况下 Builder 模式生成的实例构成了 Composite 模式。
  • Abstract Factory 模式 :Abstract Factory 模式 和 Builder 模式都用于生成复杂实例。
  • Facade 模式:在 Builder 模式中,Director 角色通过组合 Builder 角色中的复杂方法向外部提供可以简单生成实例的接口,Facade 模式中的 Facade 角色则是通过组合内部模块向外部提供可以简单调用的接口。

五、Abstract Factory 模式

Abstract Factory 模式 :将关联零件组装成产品

本部分内容推荐阅读 : 抽象工厂模式在spring源码中的应用,以下部分内容参考该文。

1. 介绍

抽象工厂会将抽象零件组装成抽象产品。也就是说,我们并不关心零件的具体实现,而是只关心接口。我们仅使用该接口欧将零件组装成为产品。

在 Template Method 模式和 Builder 模式中,子类这一层负责方法的具体实现。在 Abstract Factory 模式中也是一样的。在子类这一层中有具体的工厂,它负责将具体的零件组装成为具体的产品。


Abstract Factory 模式 登场的角色如下:

  • AbstractProduct (抽象产品):该角色负责定义 AbstractFactory角色所生成的抽象零件和产品的接口。
  • AbstractFactory(抽象工厂):该角色负责定义用于生成抽象产品的接口
  • Client(委托者):该角色仅会调用 AbstractFactory 角色和 AbstractProduct 角色的接口来进行工作,对于具体的零件、产品和工厂一无所知。
  • ConcreteProduct (具体产品):该角色负责实现 AbstractProduct 角色的接口。
  • ConcreteFactory (具体工厂):该角色负责实现 AbstractFactory 角色的接口。

类图如下:
在这里插入图片描述


Demo 如下:

// 抽象罐头工厂类
public abstract class CanFactory {

    /**
     * 获取工厂实例
     * @param className
     * @return
     * @throws Exception
     */
    public CanFactory getCanFactory(String className) throws Exception {
        final Class<?> factoryClass = Class.forName(className);
        return (CanFactory) factoryClass.newInstance();
    }

    /**
     * 包装苹果罐头
     */
    public abstract AppleCan packAppleCan();

    /**
     * 包装牛肉罐头
     */
    public abstract BeefCan packBeefCan();

}

// 互联网罐头工厂
public class InternetCanFactory extends CanFactory {
	// 包装苹果罐头
    @Override
    public AppleCan packAppleCan() {
        return new BaiduAppleCan();
    }
	// 包装牛肉贯通
    @Override
    public BeefCan packBeefCan() {
        return new TencentBeefCan();
    }
}

// 牛肉罐头抽象类
public abstract class BeefCan {
    /**
     * 闻起来如何
     * @return
     */
    public abstract String smell();
}

// 苹果罐头抽象类
public abstract class AppleCan {
    /**
     * 吃起来如何
     * @return
     */
    public abstract String taste();
}

// 罐头具体实现类
public class BaiduAppleCan extends AppleCan {
    
    @Override
    public String taste() {
        return "牛肉真香";
    }
}

public class TencentBeefCan extends BeefCan {
    
    @Override
    public String smell() {
        return "苹果真甜";
    }
}

在上面的 Demo 中:CanFactory、AppleCan 和 BeefCan 的关系已经确定, CanFactory 用来生产 AppleCan 和 BeefCan ,而 InternetCanFactory 作为 CanFactory 的实现类,可以生产 百度苹果罐头 和 腾讯牛肉罐头 。即抽象层定义工厂框架,实现层进行具体的实现。


2. 应用

在 Spring 中,BeanFactory 是用于管理 Bean 的一个工厂,所有工厂都是 BeanFactory 的子类。这样我们可以通过 IOC 容器来管理访问 Bean,根据不同的策略调用 getBean() 方法,从而获得具体对象。



个人使用:该部分内容是写给自己看的,帮助自身理解,因此就不交代项目背景了,读者请自行忽略(◐ˍ◑)

  • 在项目A中,需要对多种文件类型资料进行解析,而解析后的结果是相同的,则可以通过抽象工厂模式为每种文件资料实现一个工厂类。如下:

    public class FileHandlerFactory {
        /**
         * pdf
         */
        public static final String FILE_PDF = "pdf";
        /**
         * excel
         */
        public static final String FILE_EXCEL = "excel";
    
        /**
         * 获取处理器
         * @param fileType
         * @return
         */
        public static FileHandler getFileHandler(String fileType) {
            if (FILE_PDF.equalsIgnoreCase(fileType)) {
                return new PdfFileHandler();
            } else if (FILE_EXCEL.equalsIgnoreCase(fileType)) {
                return new ExcelFileHandler();
            }
            return null;
        }
    
    }
    
    public abstract class FileHandler {
        /**
         * 处理
         */
       abstract void handle();
    }
    
    public class ExcelFileHandler extends FileHandler {
        @Override
        void handle() {
            System.out.println("ExcelFileHandler.handle");
        }
    }
    
    public class PdfFileHandler extends FileHandler{
        @Override
        void handle() {
            System.out.println("PdfFileHandler.handle");
        }
    }
    
    public class DemoMain {
        public static void main(String[] args) {
            FileHandler pdfHandler = FileHandlerFactory.getFileHandler(FileHandlerFactory.FILE_PDF);
            FileHandler excelHandler = FileHandlerFactory.getFileHandler(FileHandlerFactory.FILE_EXCEL);
    
            pdfHandler.handle();
            excelHandler.handle();
        }
    }
    

3. 总结

优点 : 抽象工厂模式隔离了具体类的生成, 使得客户并不需要知道什么被创建。 由于这种隔离,更换一个具体工厂就变得相对容易, 所有的具体工厂都实现了抽象工厂中定义的那些公共接口, 因此只需改变具体工厂的实例, 就可以在某种程度上改变整个软件系统的行为。当一个族中的多个对象被设计成一起工作时, 它能够保证客户端始终只使用同一个族中的对象。增加新的族很方便, 无须修改已有系统, 符合“开闭原则”。

缺点 : 增加新的等级结构麻烦, 需要对原有系统进行较大的修改, 甚至需要修改抽象层代码,这显然会带来较大的不便, 违背了“开闭原则”。

使用场景 : 一个系统不应当依赖于具体类实例如何被创建、 组合和表达的细节, 这对于所有类型的工厂模式都是很重要的, 用户无须关心对象的创建过程, 将对象的创建和使用解耦;系统中有多于一个的族, 而每次只使用其中某一族。 可以通过配置文件等方式来使得用户可以动态改变族, 也可以很方便地增加新的族。属于同一个族的对象将在一起使用, 这一约束必须在系统的设计中体现出来。 同一个族中的对象可以是没有任何关系的对象, 但是它们都具有一些共同的约束, 如同一操作系统下的按钮和文本框, 按钮与文本框之间没有直接关系, 但它们都是属于某一操作系统的, 此时具有一个共同的约束条件: 操作系统的类型。等级结构稳定, 设计完成之后, 不会向系统中增加新的等级结构或者删除已有的等级结构。


相关设计模式:

  • Builder 模式 : Abstract Factory 模式是通过调用抽象产品的接口来组装抽象产品,生成具有复杂结构的实例。Builder 模式 则是分阶段地制作复杂的实例。
  • Factory Method 模式:Abstract Factory 模式在零件和产品生成时可能会使用到该模式
  • Composite 模式:Abstract Factory 模式在制作产品时可能会使用该模式
  • Singleton 模 :Abstract Factory 模式的具体工厂时可能会使用该模式

扩展思路:

  • Abstract Factory 模式非常方便增加具体的工厂,即是修复具体工厂的Bug,都无需修改抽象工厂和调用部分。但是相对的,该模式难以增加新工厂方法,如果要增加新的工厂方法则需要将已经实现的具体工厂进行同步改造。

参考内容

抽象工厂模式在spring源码中的应用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1360119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LiveGBS流媒体平台GB/T28181常见问题-国标编号是什么设备编号和通道国标编号标记唯一的摄像头|视频|镜头通道

LiveGBS国标GB28181中国标编号是什么设备编号和通道国标编号标记唯一的摄像头|视频|镜头通道 1、什么是国标编号&#xff1f;2、国标设备ID和通道ID3、ID 统一编码规则4、搭建GB28181视频直播平台 1、什么是国标编号&#xff1f; 国标GB28181对接过程中&#xff0c;可能有的小…

flutter版本升级后,解决真机和模拟器运行错误问题

flutter从3.3.2升级到3.16.0&#xff0c;项目运行到真机和模拟器报同样的错&#xff0c;错误如下: 解决办法&#xff1a;在android目录下的build.gradle加入下面这行&#xff0c;如下图&#xff1a; 重新运行&#xff0c;正常把apk安装到真机上或者运行到模拟器上

PC+Wap仿土巴兔装修报价器源码 PHP源码

核心功能&#xff1a; 业主自助预算计算&#xff1a;通过简洁的界面&#xff0c;业主可以输入装修需求&#xff0c;系统自动进行预算计算信息自动收集&#xff1a;系统自动收集业主的基本信息&#xff0c;如姓名、联系方式、房屋面积等一键发送报价&#xff1a;业主完成预算计…

Apache Doris 2.0.2 安装步骤 Centos8

Linux 操作系统版本需求 Linux 系统版本当前系统版本CentOS7.1 及以上CentOS8Ubuntu16.04 及以上- 软件需求 软件版本当前版本Java1.81.8.0_391GCC4.8.2 及以上gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-4) 1、查看操作系统版本 方法 1&#xff1a;使用命令行 打开终端或…

springboot第46集:Nginx,Sentinel,计算机硬件的介绍

image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png image.png 什么是单点容错率低&#xff1a; 单点容错率低指的是系统中存在某个关键节点&#xff0c;一旦这个节点发生故障或崩…

Apache SeaTunnel:探索下一代高性能分布式数据集成工具

大家下午好&#xff0c;我叫刘广东&#xff0c;然后是来自Apache SeaTunnel社区的一名Committer。今天给大家分享的议题是下一代高性能分布式海量数据集成工具&#xff0c;后面的整个的PPT&#xff0c;主要是基于开发者的视角去看待Apache SeaTunnel。后续所有的讲解主要是可能…

基于spark的Hive2Pg数据同步组件

一、背景 Hive中的数据需要同步到pg供在线使用&#xff0c;通常sqoop具有数据同步的功能&#xff0c;但是sqoop具有一定的问题&#xff0c;比如对数据的切分碰到数据字段存在异常的情况下&#xff0c;数据字段的空值率高、数据字段重复太多&#xff0c;影响sqoop的分区策略&…

飞塔FortiGate-1000C设备引进助力易天构建网络安全新防线

在当今数字化浪潮的推动下&#xff0c;企业对网络安全的需求日益迫切。为了应对不断升级的网络威胁&#xff0c;给客户提供最为优质的产品&#xff0c;易天引进了最新兼容性测试设备飞塔FortiGate-1000C&#xff0c;为光模块产品交付提供了更强劲的性能保障。 FortiGate-1000C是…

filecoin通过filutils 区块浏览器获取历史收益数据

filecoin 历史收益数据 每天每T平均收益 导出历史每日收益为文档 filutils 区块浏览器 导出历史每日收益为文档 #!/bin/bashfor i in {1..10} doecho $iresult$(curl --location --request POST https://api.filutils.com/api/v2/powerreward \--header User-Agent: Apifox/1.…

fmincon函数求解非线性超越方程的学习记录

最近的算法中用到了fmincon函数&#xff0c;寻找多变量非线性方程最小值的函数&#xff1b;因此学习一下&#xff1b; fmincon函数的基础语法如下所示&#xff1a; fmincon函数是为了求解下列方程的最小值&#xff1b; b 和 beq 是向量&#xff0c;A 和 Aeq 是矩阵&#xff0c…

企业级大数据安全架构(二)安全方案

作者&#xff1a;楼高 1 Knox访问控制 Apache Knox是一个为Apache Hadoop部署提供交互的应用网关&#xff0c;通过其REST API和用户友好的UI&#xff0c;为所有与Hadoop集群的REST和HTTP交互提供了统一的访问点。Knox不仅仅是一个访问网关&#xff0c;它还具备强大的访问控制…

(2024,少样本微调自适应,泛化误差界限,减小泛化误差的措施)多模态基础模型的少样本自适应:综述

Few-shot Adaptation of Multi-modal Foundation Models: A Survey 公和众和号&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 1. 简介 2. 多模态基础模型的预训练 3. 多模态基础模…

关于kthread_stop的疑问(linux3.16)

线程一旦启动起来后&#xff0c;会一直运行&#xff0c;除非该线程主动调用do_exit函数&#xff0c;或者其他的进程调用kthread_stop函数&#xff0c;结束线程的运行。 之前找销毁内核线程的接口时&#xff0c;发现了kthread_stop这个接口。网上说这个函数能够销毁一个内核线程…

JavaScript:构造函数面向对象

JavaScript&#xff1a;构造函数&面向对象 构造函数实例化静态成员实例成员 内置构造函数引用类型基本含义常用属性方法ObjectArray 包装类型基本含义常用属性方法StringNumber 面向对象原型对象constructor对象原型原型链原型继承 构造函数 在讲解构造函数之前&#xff0…

[NISACTF 2022]bingdundun~

[NISACTF 2022]bingdundun~ wp 信息搜集 进入题目&#xff1a; 点一下 upload? &#xff1a; 注意看上面的 URL &#xff0c;此时是 ?bingdundunupload 。 随便找个文件上传一下&#xff1a; 注意看上面的 URL &#xff0c;此时变成&#xff1a;upload.php 。 那么我有理…

【力扣算法日记】无重复字符的最长子串

最近刷了很多算法题&#xff0c;这些解题过程也拓展了自己的思路&#xff0c;是个适合记录的素材。所以决定在继技术知识点详解的【一文系列】之后&#xff0c;开启新坑——【力扣算法系列】&#xff0c;来记录力扣刷题过程。 分享题目不确定&#xff0c;目前打算只分享我认为…

聊一聊 .NET高级调试 内核模式堆泄露

一&#xff1a;背景 1. 讲故事 前几天有位朋友找到我&#xff0c;说他的机器内存在不断的上涨&#xff0c;但在任务管理器中查不出是哪个进程吃的内存&#xff0c;特别奇怪&#xff0c;截图如下&#xff1a; 在我的分析旅程中都是用户态模式的内存泄漏&#xff0c;像上图中的…

【JVM】类加载器ClassLoader

一、简介 在Java中&#xff0c;类加载器&#xff08;ClassLoader&#xff09;是一个关键的组件&#xff0c;它负责将字节码文件加载到内存并转换成Java类。Java的类加载器主要可以分成两类&#xff1a;系统提供的和由Java应用开发人员编写的。Java开发者可以根据需要创建自己的…

ES集群分片数据的高可用

文章目录 ES集群分片数据的高可用1. 集群设置索引节点2. 集群新增文档数据3.查看集群中文档数据分片节点4. 让节点9201宕机&#xff0c;查看其分片变化5. 让节点9201&#xff0c;查看分片变化 ES集群分片数据的高可用 集群中的索引主分片和副分片在不同的计算机上&#xff0c;如…

揭开 JavaScript 作用域的神秘面纱(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…