三大工厂设计模式

1.简单工厂模式

1.1需求入手

        从需求进行入手,可以更深入的理解什么是设计模式。

        有一个制作披萨的需求:需要便于扩展披萨的种类,便于维护。

        1.披萨的种类有很多:GreekPizz,CheesePizz等

        2.披萨的制作流程:prepare(制作)=> bake(烘烤)=> cut(切开)=> box(打包)

        3.完成披萨店的订购功能。

1.2使用传统的方式进行实现

1.2.1披萨抽象类

        进行定义抽象披萨类,这个抽象类进行定义了name属性和抽象方法prepape,这个抽象方法主要是让继承的类去实现使用,定义了一些非抽象方法,这些非抽象方法,是派生类通用的代码,都可以进行使用的这些方法,并且表现出嗯对特征是相同的。

        即将派生类需要重写表现出来不同特征的方法在抽象类中直接定义为abstract抽象方法,所有子类表现出来相同特征不需要进行定制化的代码直接定义为普通方法,放在抽象类中进行使用。

        这里由于披萨的制作流程,prepare => bake => cut => box,整个过程中只有prepare的过程是不一样的,其它几个方法都是一样的,所以后面的方法都直接在抽象类中进行定义了,这些方法都直接在抽象类中进行实现了逻辑,将prepare定义为abstract方法,方法派生类进行实现。

/*** 披萨类*/
public abstract class Pizza {protected String name;public abstract void prepare();// 烘烤public void bake() {System.out.println(name + " baking~~~");}// 切分public void cut() {System.out.println(name + " cutting~~~");}// 打包public void box() {System.out.println(name + " boxing~~~");System.out.println("bing go 完成啦~~~");}public void setName(String name) {this.name = name;}}

1.2.2两个Pizza实现类

/*** GreekPizza*/
public class GreekPizza extends Pizza {@Overridepublic void prepare() {System.out.println("为希腊披萨 准备原材料");}
}
/*** 奶酪披萨*/
public class CheesePizza extends Pizza {@Overridepublic void prepare() {System.out.println("给制作奶酪披萨 准备原材料");}
}

1.2.3定义Pizza下单类

        这个类中进行封装了一个getType方法,这个方法主要是进行在控制台进行接收数据并返回。

        这个类的主要逻辑封装在了Constructor中,进行循环在控制台中进行接收用户输入到控制台中的数据,并进行根据输入的数据进行采用一些逻辑操作,如果输入的披萨是有的就进行new一个相应的披萨对象,并进行走相应的制作流程,如果输入的披萨都不存在,就会break跳出循环,结束点单。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;/*** 订购披萨*/
public class OrderPizza {public OrderPizza() {Pizza pizza = null;// 订购披萨的类型String orderType = null;while (true) {orderType = getType();if (orderType.equals("greek")) {pizza = new GreekPizza();pizza.setName("希腊披萨");} else if (orderType.equals("cheese")) {pizza = new CheesePizza();pizza.setName("奶酪披萨");} else {break;}// 输出pizza 制作过程pizza.prepare();pizza.bake();pizza.cut();pizza.box();}}private String getType() {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类");try {String str = bufferedReader.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}

1.2.4UML类图

1.3分析传统方法的问题

1.3.1优缺点的分析

1.3.1.1传统方式的优点

        优点就是比较好理解,便于进行操作。

1.3.1.2传统方式的缺点

        缺点就是违反了OCP原则(开闭原则),对扩展(提供者)开放,对修改(使用者)关闭,从UML图中就可以进行分析出来,提供者就是Pizza类的派生类,对扩展开放的意思其实就是Pizza的派生类可以无限进行扩展,代码底层可以进行更改(只要外部调用的时候表现出来的特征一样就没问题),对修改关闭的意思就是,当Pizza进行扩展出新的派生类的时候,十一用者可以进行完美的无缝兼容,调用的方法无需进行改变,这就是OCP原则 => 至少要进行保证尽量少的进行修改代码。

1.3.1.3缺点的体现

        假设现在进行增加一个新的Pizza派生类:PepperPizza。

        此时进行的增加改动是遵循OCP开闭原则,对扩展进行开放。

/*** 胡椒披萨*/
public class PepperPizza extends Pizza {@Overridepublic void prepare() {System.out.println("给制作胡椒披萨 准备原材料");}
}

        但是如果我们想要在使用者OrderPizza中进行兼容使用,就需要在使用者OrderPizza中进行修改,这样就违反了OCP原则,对修改没有进行关闭。

public OrderPizza() {Pizza pizza = null;// 订购披萨的类型String orderType = null;while (true) {orderType = getType();if (orderType.equals("greek")) {pizza = new GreekPizza();pizza.setName("希腊披萨");} else if (orderType.equals("cheese")) {pizza = new CheesePizza();pizza.setName("奶酪披萨");} else if (orderType.equals("pepper")) {pizza = new PepperPizza();pizza.setName("胡椒披萨");} else {break;}// 输出pizza 制作过程pizza.prepare();pizza.bake();pizza.cut();pizza.box();}}

1.3.2改进的思路

        如果进行了扩展,可以接受对代码进行接单的修改(比如说对一处代码进行修改),但是我们往往不是只有一个OrderPizza下单的地方,假如说我们这些Pizza还需要在其它的地方进行使用,那么Pizza如果进行了扩展之后,就需要在多处进行修改,这样就太糟糕了。

        主要问题:主要问题就是创建Pizza的代码往往有多处,Pizza进行扩展后需要在多处进行变动修改。

        思路:将创建Pizza对象封装到一个类中(工厂类),当我们有新的Pizza类被扩展出的时候,只需要修改这个类即可,其它创建Pizza的代码就不需要进行修改了  => 简单工厂模式。

        简单工厂模式就是来进行解决这个问题的,将Pizza对象的创建交给简单工厂类,这样就将创建Pizza对象的代码从各个地方进行解耦i。

1.4简单工厂模式

1.4.1什么是简单工厂模式

        1.简单工厂模式属于创造性模式,是工厂模式中的一种。简单工厂模式是由一个工厂对象决定创建出那哪一个产品类的实例。简单工厂模式是工厂模式家族最简单实用的模式。

        2.简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)=> 其实就是将代码抽离到一个工厂类中,在工厂类中进行创建实例化对象。

        3.在软件开发中,当我们会用到大量的创建某种,某类或者某批对象时,就会使用到工厂模式。

1.4.2改进后的UML类设计图

        通过UML类设计图就可以分析出来Pizza派生出去的类都会依赖在SimpleFactory类中,也就是将所有Pizza类型的创建交给SimpleFactory简单工厂类进行处理,其它的需要创建Pizza对象的类,直接进行将SimpleFactory这个工厂类对象聚合进去使用即可。

        也就是说将创建Pizza对象的权力交给了SimoleFactory工厂类了,需要创建对象直接找他即可,完成了对象创建的解耦。

1.4.3编写简单工厂类

        其实就是将其它类创建Pizza对象的代码给集成到SimpleFactory简单工厂类中,并不仅仅局限于目前类中的createPizza方法,其它方法同样也是可以进行集成抽离的,我的理解是,只要是用到创建Pizza对象的代码都可以抽离到SimpleFactory中进行统一维护使用。

        目前进行定义的createPizza的代码其实是一个比较健全的根据传入的类型创建不同Pizza派生类对象的代码,如果有对应type就会进行创建,如果没有就不创建,返回一个null。

/*** 简单工厂类*/
public class SimpleFactory {public Pizza createPizza(String type) {System.out.println("使用简单工厂模式");Pizza pizza = null;if (type.equals("greek")) {pizza = new GreekPizza();pizza.setName("希腊披萨");} else if (type.equals("cheese")) {pizza = new CheesePizza();pizza.setName("奶酪披萨");} else if (type.equals("pepper")) {pizza = new PepperPizza();pizza.setName("胡椒披萨");}return pizza;}}

1.4.4重塑OrderPizza类

        其实就是使用聚合的方式将SimpleFactory简单工厂类集成到OrderPizza,并将创建Pizza对象的逻辑抽取到SimpleFactory中进行完成。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;/*** 订购披萨*/
public class OrderPizza {// 定义一个简单工厂对象private SimpleFactory simpleFactory;private Pizza pizza = null;public void setSimpleFactory(SimpleFactory simpleFactory) {this.simpleFactory = simpleFactory;}public void order() {// 用户输出的String orderType = "";// 进行订购披萨while (true) {orderType = getType();pizza = simpleFactory.createPizza(orderType);// 输出Pizzaif (pizza != null) {pizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println("订购披萨失败!!!");break;}}}private String getType() {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类");try {String str = bufferedReader.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}

2.工厂方法模式

2.1需求入手

2.1.1需求升级

        现在进行点披萨的需求再次升级了,主要是下单的奶酪种类进行了进一步细分,客户在点披萨的时候可以进行点北京的奶酪Pizza,北京的胡椒Pizza或者伦敦的奶酪Pizza,伦敦的胡椒Pizza。

2.1.2思路分析 => 使用简单工厂模式

        由于分类进一步细分了,使用一个SimpleFactoiy进行创建不同的Pizza,当然也可以进行根据地域分类的不同,再进行抽象创建出多个SimpleFactory,进行针对不同低于的Pizza使用不同的SimpleFactory。

        但是考虑项目的规模,以及软件的可维护性,可扩展性,使用这个方式可能会有很多SimpleFactory,会降低可维护性和扩展性,感觉不是很好。

2.1.3思路分析 => 使用工厂方法模式、

        使用工厂方法模式可以解决这个问题。

2.2工厂方法模式介绍

2.2.1设计方案

        将披萨项目的实例化Pizza的功能抽象为抽象方法,在不同的口味点餐子类中具体实现。

2.2.2什么是工厂方法模式

        定义一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法将对象的实例化推迟到子类。

2.3应用案例

2.3.1UML类图设计

2.3.2工厂方法抽象类的定义

        order和getrType都是所有工厂方法实现类都拥有的通用方法,这些方法直接定义在OrderPizza中即可。

        每个工厂方法实现类的区别主要是在创建Pizza对象的逻辑不一样,不同的工厂方法实现类负责不同类别的Pizza实例对象的创建,所以要进行定义这个createPizza的抽象方法,让不同的工厂方法实现类去实现这个abstract方法。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;/*** 定义抽象方法的工厂 => 方便实现工厂实现实例化的方法*/
public abstract class OrderPizza {// 定义一个抽象, createPizza,public abstract Pizza createPizza(String type);// 下单方法public void order() {// 用户输出的String orderType = "";// 进行订购披萨while (true) {orderType = getType();Pizza pizza = createPizza(orderType);// 输出Pizzaif (pizza != null) {pizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println("订购披萨失败!!!");break;}}}private String getType() {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类");try {String str = bufferedReader.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}

3.抽象工厂模式

3.1什么是抽象工厂模式

        1.抽象工厂和模式:定义了一个interface用于创建相关或者有依赖关系的对象簇,而无需指明具体的类。

        2.抽象工厂模式其实可以将简单工厂模式和工厂方法模式进行整合。

        3.从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者说是进一步抽象)

        4.将工厂抽象为两层:AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样单个工厂类就变为了工厂簇,更利于代码的维护和扩展。

3.2抽象工厂模式案例

3.2.1UML设计类图

        这个的设计其实就是定义出了一个AbsFactory接口,抽象出来一个创建实例的工厂方法,让不同的子类去依赖不同Pizza实现类,去实现相应的方法。

        然后让需要进行使用抽象工厂创建相应实例的类中,直接将抽象工厂方法类AbsFactory聚合进去即可,这样就实现了一层缓冲解耦合。

3.2.2定义抽象工厂方法接口

/*** 抽象工厂的抽象接口*/
public interface AbsFactory {Pizza createPizza(String orderType);}

3.2.3定义抽象工厂方法实现类

/*** 北京抽象工厂实现类*/
public class BJFactory implements AbsFactory {@Overridepublic Pizza createPizza(String orderType) {System.out.println("使用抽象工厂模式");Pizza pizza = null;if (orderType.equals("greek")) {pizza = new BJGreekPizza();pizza.setName("北京希腊披萨");} else if (orderType.equals("cheese")) {pizza = new BJCheesePizza();pizza.setName("北京奶酪披萨");} else if (orderType.equals("pepper")) {pizza = new BJPepperPizza();pizza.setName("北京胡椒披萨");}return pizza;}}

3.2.4创建对象客户端聚合抽象工厂方法实现类

        其实就是通过一个字段,定义为抽象接口类型(这样就可以接收不同类型的抽象工厂实例类对象了)

        使用setter方法进行给字段设置对应对象 => 从里面就可以体现出聚合。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;/*** 下单端(使用抽象工厂创建实例)*/
public class OrderPizza {private AbsFactory factory;public void setFactory(AbsFactory factory) {this.factory = factory;}public void order() {// 用户输出的String orderType = "";// 进行订购披萨while (true) {orderType = getType();Pizza pizza = factory.createPizza(orderType);// 输出Pizzaif (pizza != null) {pizza.prepare();pizza.bake();pizza.cut();pizza.box();} else {System.out.println("订购披萨失败!!!");break;}}}private String getType() {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("input pizza 种类");try {String str = bufferedReader.readLine();return str;} catch (IOException e) {e.printStackTrace();return "";}}}

4.工厂模式在JDK中的应用

4.1Calendar源码中的应用

        Calendar的源码主要是应用了简单工厂模式,主要是在创建对象的时候进行使用。

        Calendar中的构造方法被protected化了,所以在外部是无法通过New关键字去创建Calendar对象的,但是Calendar类对外暴露了一个getInstance方法进行使用,Calendar类就是通过getInstance方法进行创建对象的。

4.1.1Calendar中创建实例对象的代码分析

4.1.1.1被封印的构造函数

        可以看到Calendar方法确实被封印了,方法被标注为了protected,表示这个方法不能在类/派生类的外部进行使用。

/*** Constructs a Calendar with the default time zone* and the default {@link java.util.Locale.Category#FORMAT FORMAT}* locale.* @see     TimeZone#getDefault*/
protected Calendar()
{this(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));sharedZone = true;
}/*** Constructs a calendar with the specified time zone and locale.** @param zone the time zone to use* @param aLocale the locale for the week data*/
protected Calendar(TimeZone zone, Locale aLocale)
{fields = new int[FIELD_COUNT];isSet = new boolean[FIELD_COUNT];stamp = new int[FIELD_COUNT];this.zone = zone;setWeekCountData(aLocale);
}
4.1.1.2getInstancer方法创建对象

        Calendar使用getIntance进行让外部创建对象,这就是Calendar进行暴露在外部创建对象的方法(getInstance方法)

        Calendar主要也是在里面进行使用了简单工厂模式,进行创建对象。

        接下来就来分析一下getInstance的源码。

/*** Gets a calendar using the default time zone and locale. The* <code>Calendar</code> returned is based on the current time* in the default time zone with the default* {@link Locale.Category#FORMAT FORMAT} locale.** @return a Calendar.*/
public static Calendar getInstance()
{return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}/*** Gets a calendar using the specified time zone and default locale.* The <code>Calendar</code> returned is based on the current time* in the given time zone with the default* {@link Locale.Category#FORMAT FORMAT} locale.** @param zone the time zone to use* @return a Calendar.*/
public static Calendar getInstance(TimeZone zone)
{return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}/*** Gets a calendar using the default time zone and specified locale.* The <code>Calendar</code> returned is based on the current time* in the default time zone with the given locale.** @param aLocale the locale for the week data* @return a Calendar.*/
public static Calendar getInstance(Locale aLocale)
{return createCalendar(TimeZone.getDefault(), aLocale);
}/*** Gets a calendar with the specified time zone and locale.* The <code>Calendar</code> returned is based on the current time* in the given time zone with the given locale.** @param zone the time zone to use* @param aLocale the locale for the week data* @return a Calendar.*/
public static Calendar getInstance(TimeZone zone,Locale aLocale)
{return createCalendar(zone, aLocale);
}

4.1.2分析getInstance方法

        就从最简单常用的无参getInstance方法进行分析。

        可以看见其中调用了关键方法createCalendar,这个方法是实例化对象的关键。

public static Calendar getInstance()
{return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}

        Calendar整体源码还是比较清晰的,根据对源码的追溯,其中最重要的就是两点:

        1.CalendarProvider对象,这个对象还是很关键的,在createCalendar的流程中进行获取到了这个对象。

        2.调用CalendarProvider对象中的getInstance方法进行创建了一个Calendar对象。

private static Calendar createCalendar(TimeZone zone,Locale aLocale)
{CalendarProvider provider =LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();if (provider != null) {try {return provider.getInstance(zone, aLocale);} catch (IllegalArgumentException iae) {// fall back to the default instantiation}}Calendar cal = null;if (aLocale.hasExtensions()) {String caltype = aLocale.getUnicodeLocaleType("ca");if (caltype != null) {switch (caltype) {case "buddhist":cal = new BuddhistCalendar(zone, aLocale);break;case "japanese":cal = new JapaneseImperialCalendar(zone, aLocale);break;case "gregory":cal = new GregorianCalendar(zone, aLocale);break;}}}if (cal == null) {// If no known calendar type is explicitly specified,// perform the traditional way to create a Calendar:// create a BuddhistCalendar for th_TH locale,// a JapaneseImperialCalendar for ja_JP_JP locale, or// a GregorianCalendar for any other locales.// NOTE: The language, country and variant strings are interned.if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {cal = new BuddhistCalendar(zone, aLocale);} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"&& aLocale.getCountry() == "JP") {cal = new JapaneseImperialCalendar(zone, aLocale);} else {cal = new GregorianCalendar(zone, aLocale);}}return cal;
}

4.1.3分析CalendarProvider

        这里不用去关心createCalendar是怎么获取到的,只需要关心怎么用的CalendarProvider对象,内部的原理是什么即可。

        CalendarProvider其实是一个抽象类,主要是进行定义了一个关键的abstract方法getInstance,这个方法返回的是一个Calendar,主要进行用于创建Calendar实例。

/*** An abstract class for service providers that* provide instances of the* {@link java.util.Calendar Calendar} class.** @since        1.8*/
public abstract class CalendarProvider extends LocaleServiceProvider {/*** Sole constructor.  (For invocation by subclass constructors, typically* implicit.)*/protected CalendarProvider() {}/*** Returns a new <code>Calendar</code> instance for the* specified locale.** @param zone the time zone* @param locale the desired locale* @exception NullPointerException if <code>locale</code> is null* @exception IllegalArgumentException if <code>locale</code> isn't*     one of the locales returned from*     {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()*     getAvailableLocales()}.* @return a <code>Calendar</code> instance.* @see java.util.Calendar#getInstance(java.util.Locale)*/public abstract Calendar getInstance(TimeZone zone, Locale locale);
}

        CalendarProviderImpl进行实现了CalendarProvider抽象类,其中最主要的是进行实现了gtetInstance方法。

        会发现getInstance方法进行完成了将Calendar实例化并返回出去的任务。

/*** Concrete implementation of the  {@link sun.util.spi.CalendarProvider* CalendarProvider} class for the JRE LocaleProviderAdapter.** @author Naoto Sato*/
public class CalendarProviderImpl extends CalendarProvider implements AvailableLanguageTags {private final LocaleProviderAdapter.Type type;private final Set<String> langtags;public CalendarProviderImpl(LocaleProviderAdapter.Type type, Set<String> langtags) {this.type = type;this.langtags = langtags;}/*** Returns an array of all locales for which this locale service provider* can provide localized objects or names.** @return An array of all locales for which this locale service provider* can provide localized objects or names.*/@Overridepublic Locale[] getAvailableLocales() {return LocaleProviderAdapter.toLocaleArray(langtags);}@Overridepublic boolean isSupportedLocale(Locale locale) {// Support any locales.return true;}/*** Returns a new <code>Calendar</code> instance for the* specified locale.** @param zone the time zone* @param locale the desired locale* @exception NullPointerException if <code>locale</code> is null* @exception IllegalArgumentException if <code>locale</code> isn't*     one of the locales returned from*     {@link java.util.spi.LocaleServiceProvider#getAvailableLocales()*     getAvailableLocales()}.* @return a <code>Calendar</code> instance.* @see java.util.Calendar#getInstance(java.util.Locale)*/@Overridepublic Calendar getInstance(TimeZone zone, Locale locale) {return new Calendar.Builder().setLocale(locale).setTimeZone(zone).setInstant(System.currentTimeMillis()).build();}@Overridepublic Set<String> getAvailableLanguageTags() {return langtags;}
}

4.1.4图解源码 => 根据UML类图理解Calendar实例化设计

        去理解源码中的设计思想单单靠Debug和文字讲解是比较抽象的,画出来一个UML类图进行理解会比较直观,没有这么抽象。

        现在我将代码进行梳理了出来,Calendar是被依赖的类,也是进行调用创建Calendar实例对象的客户端,CalendarProviderImpl其实就是进行创建Calendar实例对象的工厂类,所以说这里进行使用的就是简单工厂设计模式。

        将创建类的能力抽离到了工厂类中。

4.2Iterator源码中的应用

4.2.1简要理解

        Iterator是在Collection整个体系中进行使用了这个Iterator,JDK整个Collection体系中进行使用到了Iterator,在这个集合体系中任何一个类型的集合都是一个工厂,接口和抽象类是抽象工厂,普通类是实际的生产对象的工厂,工厂进行创建的对象是Iteator。

        理清几个重要的关系:

        抽象工厂(定义抽象方法的接口/抽象类):接口/抽象类

        实例工厂(实际执行创建实例的工厂类):实现接口/继承抽象类的实例类

        实例化的对象:Iterator以及其实现类(整个体系)

4.2.2UML类图

5.三大工厂模式小结

5.1工厂模式的意义

        将实例化的代码提取出来,放到一个类中进行统一管理和维护,达到和主项目依赖关系的解耦。从而提高项目的扩展性和维护性。

5.2三种工厂模式

        1.简单工厂模式

        就是将一个抽象类型的类,其所有派生类实例化对象的创建均抽取到一个工厂类中,使用工厂类完成所有类实例化对象的创建。

        实现了创建对象代码的解耦,将实例化对象的逻辑全部抽取到工厂类中。

        2.工厂方法模式

        就是定义一个抽象工厂类,使用这个抽象工厂派生出各种各样的工厂实现类,进行对不同种类的对象进行实例化,客户端可以根据自己的需求去选择不同的工厂实现类,调用创建自己需要的对象。

        就是对简单工厂进行按不同类型对象的创建职责进行了进一步的拆分,不同类型的对象采用不同工厂进行创建,提高了扩展性和可维护性,适合大型项目进行使用。

        3.抽象工厂模式

        抽象工厂模式就是定义了一个抽象方法,在抽象中进行定义实例化对象的方法,工厂进行实现这个接口,实现工厂中创建哪些对象的逻辑,客户端需要进行创建对象的时候,注入这个工厂进行创建即可,需要替换的时候与,注入其它工厂即可(注入的类型为接口,方便进行替换为其它实现工厂)

5.3设计模式的依赖抽象原则

        其实三大工厂设计模式都是进行遵循设计模式的依赖抽象原则,比如说简单工厂中,工厂类就是一层抽象(抽象程度弱),工厂方法模式中的抽象工厂类也是一层抽象(使用抽象类进行架构设计,抽象程度强),抽象工厂设计模式也是一层抽象,使用接口进行抽象出相应的对象实例化的抽象方法,接口就是相应的抽象(抽象能力强)

5.4工厂模式总结

5.4.1创建对象实例小结

        创建对象实例时,不要直接new实例,而是将这个new类的动作放在一个工厂方法中,并进行返回这个对象实例。

5.4.2实例化对象类体系的设定

        实例化对象的类体系最好是进行抽象出来一个抽象类或者接口进行实现/继承,不要让工厂创建的对象的类型是类继承的具体的类的类型,要化具体为抽象,类型应该是抽象的,不应该是具体的。

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

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

相关文章

SpringBoot--Mapper XML 和 Mapper 接口在不同包

&#x1f9e9; 背景说明在 Spring Boot 中&#xff0c;MyBatis 默认要求 Mapper 接口和 XML 文件位于相同包路径。 但在实际项目中&#xff0c;为了模块化或结构清晰&#xff0c;常将 XML 放在 resources/mybatis/... 下&#xff0c;这种做法就必须进行额外配置。&#x1f4c1;…

公交车客流人数统计管理解决方案:智能化技术与高效运营实践

1. 引言公交车作为城市公共交通的核心组成部分&#xff0c;其客流数据的精准统计与管理直接影响运营效率、调度优化和乘客体验。传统的人工统计方式效率低、误差大&#xff0c;难以满足现代智慧交通的需求。随着人工智能&#xff08;AI&#xff09;、物联网&#xff08;IoT&…

正则表达式完全指南:从入门到实战

目录 一、什么是正则表达式&#xff1f; 二、基础语法速查表 三、进阶特性 1.分组与捕获 2.非捕获分组 3.前瞻与后顾 4.贪婪与懒惰匹配 四、实战案例 案例1&#xff1a;验证手机号 案例2&#xff1a;提取网页中所有链接 案例3&#xff1a;密码强度验证 一、什么是正…

SmartETL循环流程的设计与应用

1. 引言 **检索增强生成&#xff08;RAG&#xff09;**是指通过检索对大模型生成进行增强的技术&#xff0c;通过充分利用信息检索&#xff08;尤其是语义检索&#xff09;相关技术&#xff0c;实现大模型快速扩展最新知识、有效减少幻觉的能力。主流RAG框架包括问题理解、知识…

uni-app开发小程序,根据图片提取主题色值

需求&#xff0c;在页面根据传入的图片提取图片主色值并用来设置区块背景色<template><view class"icon-container"><view class"sport-icon" :style"{ backgroundColor: mainColor }"><image :src"/static/images/sp…

ESP32-Cam三脚架机器人:DIY你的智能移动监控平台

项目概述 在物联网与机器人技术融合发展的今天&#xff0c;基于ESP32的创意项目层出不穷。今天为大家介绍一款极具创新性的ESP32-Cam三脚架机器人&#xff08;Dodge&#xff09;&#xff0c;它将传统三脚架结构与智能监控功能完美结合&#xff0c;通过巧妙的机械设计和开源硬件…

Kotlin集合过滤

过滤操作 在处理集合时&#xff0c;根据特定条件过滤集合或检查集合中是否包含符合特定条件的元素是软件开发中的常见任务。为了解决这个问题&#xff0c;我们可以使用 Kotlin 中实现的函数式 API。 在本主题中&#xff0c;我们将介绍如何使用谓词过滤集合&#xff0c;并获得满…

14.8 LLaMA2-7B×Dolly-15K实战:从准确率63%到89%,如何用优质数据让大模型性能飙升42%?

LLaMA2-7BDolly-15K实战:从准确率63%到89%,如何用优质数据让大模型性能飙升42%? 在大模型微调中,“数据质量”往往比“数据数量”更能决定最终效果。Databricks发布的Dolly-15K数据集以“全人工标注+多维度校验”的特点,成为指令微调的“黄金样本”——用它微调后的LLaMA…

OpenCV中常用特征提取算法(SURF、ORB、SIFT和AKAZE)用法示例(C++和Python)

OpenCV 中提供了多种常用的特征提取算法&#xff0c;广泛应用于图像匹配、拼接、SLAM、物体识别等任务。以下是 OpenCV 中几个主流特征提取算法的 用法总结与代码示例&#xff0c;涵盖 C 和 Python 两个版本。常用特征提取算法列表算法特点是否需额外模块SIFT&#xff08;尺度不…

复杂度+包装类型+泛型

什么是集合框架什么是数据结构什么是算法时间复杂度与空间复杂度的概念时间复杂度的表达方式时间复杂度的大 O 的渐近表示法时间复杂度函数的大小排序包装类和泛型基本数据类型和对应的包装类型包装类型出现的原因什么叫做装箱&#xff08;装包&#xff09;和拆箱&#xff08;拆…

硬件设计学习DAY15——自举电容:MOSFET高端驱动的核心奥秘

每日更新教程&#xff0c;评论区答疑解惑&#xff0c;小白也能变大神&#xff01;" 目录 一.自举电容 1.自举电容的作用 2.自举电路原理 3.工作过程分析 4.实际应用中的问题 5.关键要点 二.自举电容实现MOSFET高端驱动 2.1MOSFET半桥高端驱动的基本原理 2.2自举电…

【SpringAI实战】实现仿DeepSeek页面对话机器人

一、实现效果 二、代码实现 2.1 后端代码 2.2 前端代码 一、实现效果 可以保存聊天记录与会话记录 二、代码实现 2.1 后端代码 pom.xml <!-- 继承Spring Boot父POM&#xff0c;提供默认依赖管理 --><parent><groupId>org.springframework.boot</grou…

RedisJSON 指令精讲JSON.STRLEN 高效统计字符串长度

1 场景与价值 在日志累加、指标采集、消息追踪等场景中&#xff0c;我们常需快速判断某个字符串字段“到底有多长”&#xff0c;以便&#xff1a; 阻止过大日志&#xff1a;若长度超限则截断或归档&#xff1b;动态分桶&#xff1a;按长度选择不同存储策略&#xff1b;性能监控…

大数据量查询计算引发数据库CPU告警问题复盘

大数据量查询计算引发数据库CPU告警问题复盘一、背景二、根因分析三、解决方案方案1&#xff1a;多线程缓存方案2&#xff1a;利用中间表缓存四、总结一、背景 2025年7月份某天&#xff0c;CDP系统每天不定时推送我们的Portal服务&#xff0c;生产环境运营看板会展示统计数据&…

2025最新版虚幻引擎5(UE5)C++入门教程:前言——你的随身教程和学习笔记

大家好&#xff0c;我是开发游戏的老王&#xff0c;一名高校教师&#xff0c;我主讲游戏开发已有十余年时间&#xff0c;通过我的博客大家应该可以了解我所涉猎的游戏技术范畴非常广泛&#xff0c;除了Unreal,Unity,Godot等主流游戏引擎&#xff0c;还包括Blender、Houdini、3D…

(3)重定向 | 时间相关指令 | 文件查找 | 打包与压缩

Ⅰ . 初始重定向01 输出重定向 >在上一节中我们为了方便讲解 head 和 tail 指令&#xff0c;我们用到了 > 去生成了一千行文本。通过 > 将生成的一千行文本写入到了 large.txt 中……我们现在来正式介绍一下&#xff1a;$ echo "内容" > [目标] 本来应…

DTH11测量温湿度学习(第十一天)

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-削好皮的Pineapple! &#x1f468;‍&#x1f4bb; hello 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 削好皮的Pineapple! 原创 &#x1f468;‍&#x1f4…

Go语言初识--标识符 可见性

Go语言初识–标识符 可见性 和C语言相似&#xff0c;go语言的基本组成有&#xff1a; 包声明&#xff0c;编写源文件时&#xff0c;必须在非注释的第一行指明这个文件属于哪个包&#xff0c;如package main。引入包&#xff0c;其实就是告诉Go 编译器这个程序需要使用的包&…

Python实例之画小猪佩奇

效果图&#xff1a;python代码以及解释&#xff0c;没有运用模块。 """ 绘制小猪佩奇 """ from turtle import *def nose(x,y):"""画鼻子"""penup()# 将海龟移动到指定的坐标goto(x,y)pendown()# 设置海龟的方向&…

Unity笔记——事件中心

事件中心是什么事件中心是 Unity 游戏开发中常用的架构设计&#xff0c;它基于观察者模式 或 发布-订阅模式&#xff0c;通过委托和事件构建的一种消息管理系统。主要用于降低代码耦合度&#xff0c;实现模块间的松耦合通信的消息处理系统能大幅提升代码的可维护性和扩展性&…