当前位置: 首页 > news >正文

学习设计模式《六》——抽象工厂方法模式

一、基础概念

        抽象工厂模式的本质是【选择产品簇(系列)的实现】;

        抽象工厂模式定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

        抽象工厂模式功能:抽象工厂的功能是为一系列相关对象或相互依赖对象创建一个接口【抽象工厂其实是一个产品簇(系列)】【特别注意:这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法】;
         抽象工厂通常实现为接口;
         切换产品簇(系列):由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品构成来了一个系列或产品簇; 这就带来了很大的灵活性,即切换一个产品簇的时候,只要提供不同的抽象工厂实现就可以。

抽象工厂方法模式优缺点
序号抽象工厂方法模式优点抽象工厂方法模式缺点
1

分离接口和实现

(客户端使用抽象工厂来创建需要的对象,而客户端根本不知道具体的实现是谁,客户端只是面向产品的接口编程而已)

 不太容易扩展新的产品

(典型的抽象工厂方法若需要给整个产品新添加一个产品,那就需要修改抽象工厂,这样会导致修改所有的工厂实现类;可扩展的抽象工厂可以解决新增产品问题,但是又不够安全)

2

使得切换产品系列变得容易

(因为一个具体的工厂实现代表一个产品系列【如:Schema1代表装机方案一:Intel的CPU+技嘉的主板;
Schema2代表装机方案二:AMD的CPU+微星的主板;客户选用不同的模式,就相当于切换不同的产品系列】)

容易造成类层次复杂

(在使用抽象工厂模式时,若需要选择的层次过多,那么会造成整个类层次变得复杂)

        何时选用抽象工厂模式?

                1、如果希望一个系统独立于它的产品创建、组合和表示的时候【即:希望一个系统只知道产品的接口,而不关心实现】;

                2、如果一个系统要由多个产品系列中的一个来配置的时候【即:可以动态的切换多个产品簇(系列)】;

                3、如果要强调一系列相关产品的接口,以便联合使用它们的时候。       

二、抽象工厂方法模式示例

        业务需求比如我们需要组装电脑,在组装前我们需要选择一系列相关的配件(如:CPU、主板、内存条、硬盘、电源、机箱、音箱、键盘、鼠标、显示器等);但是我们在选择这些配件的时候会面临一系列的问题(如:品牌、型号、频率等问题确定);其次在最终确定装机方案之前,还需要考虑各个配件的兼容性(如CPU的针脚与主板的插槽数量是否匹配、购买的硬盘与主板接口是否匹配等,否则就会导致无法组装)【也就是说:装机方案是一个整体,方案里面的各个配件是相互关联的】;用户选好装机方案与配件后,给到装机工程师进行组装(即:装机工程师只是按照客户的装机方案去获取相应配件进行组装)

为了说明抽象工厂方法,我们以下的内容简单的以组装电脑所需的CPU与主板为例进行示意说明:

 2.1、不用任何设计模式的示例

1、定义CPU的接口,约束对应的功能内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// CPU接口/// </summary>internal interface ICPU{//CPU具有运算功能void Calculate();}//Interface_end
}

2、编写CPU的具体产品实现(如:Intel与AMD)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// Intel的CPU/// </summary>internal class IntelCPU : ICPU{private int pins = 1151;public IntelCPU(){}public IntelCPU(int pins){this.pins = pins;}public void Calculate(){Console.WriteLine($"Intel的CPU,针脚数为【{pins}】");}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{internal class AmdCPU : ICPU{private int pins = 940;public AmdCPU(){}public AmdCPU(int pins){this.pins = pins;}public void Calculate(){Console.WriteLine($"AMD的CPU,针脚数为【{pins}】");}}//Class_end
}

3、定义主板的接口,约束对应的功能内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// 主板接口/// </summary>internal interface IMainboard{//可安装CPU功能void InstallCPU();}//Interface_end
}

4、编写主板产品的具体实现 (如:技嘉与微星主板)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{internal class GAMainboard : IMainboard{private int cpuHoles = 940;public GAMainboard(){}public GAMainboard(int cpuHoles){this.cpuHoles = cpuHoles;     }public void InstallCPU(){Console.WriteLine($"技嘉主板,主板插槽数为【{cpuHoles}】");}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{internal class MSIMainboard : IMainboard{private int cpuHoles = 1151;public MSIMainboard(){}public MSIMainboard(int cpuHoles){this.cpuHoles = cpuHoles;}public void InstallCPU(){Console.WriteLine($"微星主板,主板插槽数为【{cpuHoles}】");}}//Class_end
}

5、分别创建CPU与主板的工厂

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// 创建CPU的简单工厂/// </summary>internal class CPUFactory{/// <summary>/// 创建CPU对象方法/// </summary>/// <param name="type">CPU类型【1表示Intel;2表示AMD】</param>/// <returns></returns>public static ICPU CreateCPU(int type){ICPU cpu = null;switch (type){case 1:cpu = new IntelCPU();break;case 2:cpu = new AmdCPU();break;default:break;}return cpu;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// 创建主板的简单工厂/// </summary>internal class MainboardFactory{/// <summary>/// 创建主板对象方法/// </summary>/// <param name="type">主板类型(1表示技嘉主板;2表示微星主板)</param>/// <returns></returns>public static IMainboard CreateMainboard(int type) {IMainboard mainboard = null;switch (type){case 1:mainboard = new GAMainboard();break;case 2:mainboard = new MSIMainboard();break;default:break;}return mainboard;} }//Class_end
}

6、编写装机工程师类实现组装电脑功能

/***
*	Title:"设计模式" 项目
*		主题:装机工程师
*	Description:
*	    功能:客户告诉装机工程师自己选择的配件,让装机工程师组装;这样存在两个问题:
*           1、对于装机工程师,只知道CPU和主板的接口,但是不知道具体的实现
*           2、CPU与主板是需要相互匹配接口的,否则无法安装使用;但目前并没有维护这种关系,是客户随意选择的
*Date:2025
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:***/using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// 装机工程师/// </summary>internal class InstallationEngineer{//定义装机需要的CPUprivate ICPU cpu = null;//定义装机需要的主板private IMainboard mainboard = null;/// <summary>/// 组装电脑/// </summary>/// <param name="cpuType">cpu类型</param>/// <param name="mainboardType">主板类型</param>public void InstallComputer(int cpuType,int mainboardType){//1、准备装机所需的配件PrepareHardwares(cpuType,mainboardType);//2、组装电脑//3、测试组装电脑的各个功能是否正常//4、交付客户}//准备装机的配件private void PrepareHardwares(int cpuType, int mainboardType){//直接找工厂获取对应的CPU与主板this.cpu = CPUFactory.CreateCPU(cpuType);this.mainboard=MainboardFactory.CreateMainboard(mainboardType);//测试配件是否可用this.mainboard.InstallCPU();this.cpu.Calculate();}}//Class_end
}

7、客户端通过装机工程师来组装电脑

using AbstractFactoryPattern.AbstractFactory;
using AbstractFactoryPattern.DAO;
using AbstractFactoryPattern.ExtandAbstractFactory;namespace AbstractFactoryPattern
{internal class Program{static void Main(string[] args){InstallationEngineerTest();Console.ReadLine();}/// <summary>/// 测试装机工程师组装的电脑/// </summary>private static void InstallationEngineerTest(){Console.WriteLine($"\n测试装机工程师组装电脑——未使用抽象工厂模式");//创建装机工程师对象InstallationEngineer installationEngineer=new InstallationEngineer();//客户告诉装机工程师自己选择的配件,让装机工程师组装installationEngineer.InstallComputer(1,1);}}//Class_end
}

运行结果如下:

        

目前存在的问题:

        我们可以看到虽然通过简单工厂可以组装电脑了,但是对与装机工程师来说,它只知道CPU和主板的接口,而不知道具体的实现;他也无法知道CPU与主板的匹配关系【简单工厂并没有维护类似针脚匹配的关系内容】;这样就会存在CPU于主板的针脚数不同,从而导致电脑根本无法组装的问题出现; 我们可以使用抽象工厂模式来维护各个配件的关系。

 2.2、使用抽象工厂方法模式的示例

        由于装机工程师要组装电脑对象,需要相应的配件对象(如:CPU、主板等配件),我们就可以创建一个抽象工厂给装机工程师使用,在这个抽象工厂里面定义抽象地创建CPU与主板配件的方法(即:这个抽象的工厂就相当于一个抽象的装机方案,在这个方案里面各个配件都是可以相互匹配的)

1、创建抽象工厂接口用来约束需相互匹配关联的对象

/***
*	Title:"设计模式" 项目
*		主题:典型抽象工厂
*	Description:
*	    功能:抽象工厂模式的本质是【选择产品簇(系列)的实现】
*	        抽象工厂模式定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
*	        
*	        抽象工厂的功能:抽象工厂的功能是为一系列相关对象或相互依赖对象创建一个接口【抽象工厂其实是一个产品系列】
*	                【特别注意:这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法】
*	        抽象工厂通常实现为接口;
*	        切换产品系列:由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品构成来了一个系列或产品簇;
*	                    这就带来了很大的灵活性,即切换一个产品簇的时候,只要提供不同的抽象工厂实现就可以
*	                    
*	    
*Date:2025
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:***/using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.AbstractFactory
{/// <summary>/// 抽象工厂接口,声明创建抽象产品对象的操作/// </summary>internal interface IInstallationSchema{//创建CPU对象ICPU CreateCPU();//创建主板对象IMainboard CreateMainboard();}//Interface_end
}

2、创建具体类继承抽象工厂接口真正实现所需对象且维护好相互匹配关系

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.AbstractFactory
{/// <summary>/// 装机方案一:Intel的CPU+技嘉的主板/// 这里创建CPU和主板的时候,统一为1151接口匹配对应上的,不会出问题/// </summary>internal class InstallationSchema1 : IInstallationSchema{public ICPU CreateCPU(){return new IntelCPU(1151);}public IMainboard CreateMainboard(){return new GAMainboard(1151);}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.AbstractFactory
{/// <summary>/// 装机方案二:AMD的CPU+微星的主板/// 这里创建CPU和主板的时候,统一为940接口匹配对应上的,不会出问题/// </summary>internal class InstallationSchema2 : IInstallationSchema{public ICPU CreateCPU(){return new AmdCPU(940);}public IMainboard CreateMainboard(){return new MSIMainboard(940);}}//Class_end
}

3、再来实现装机工程师类来组装电脑

        这里实现的装机工程师类与前面简单工厂的装机工程师类相比,主要的变化是:不再由客户端出入CPU与主板的参数;而是直接传入具体的装机方案(都是一套相互匹配产品簇(系列)),这样就避免了单独选择CPU与主板而出现的不匹配无法组装问题:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.AbstractFactory
{internal class InstallationEngineer2{//定义获取组装电脑所需的CPUprivate ICPU cpu;//定义获取组装电脑所需的主板private IMainboard mainboard;/// <summary>/// 装机工程师组装电脑/// </summary>/// <param name="installationSchema">装机方案</param>public void InstallComputer(IInstallationSchema installationSchema){//1、准备好装机所需的配件PrepareHardwares(installationSchema);//2、组装电脑//3、测试电脑//4、交付客户}//准备装机所需的配件private void PrepareHardwares(IInstallationSchema installationSchema){//这里需要CPU与主板的具体对象,可工程师并不知道如何创建,该怎么办?//可以使用抽象工厂来获取相应的对象this.cpu=installationSchema.CreateCPU();this.mainboard=installationSchema.CreateMainboard();//测试配件是否正常this.mainboard.InstallCPU();this.cpu.Calculate();}}//Class_end
}

4、客户端创建装机工程师来组装电脑

using AbstractFactoryPattern.AbstractFactory;
using AbstractFactoryPattern.DAO;
using AbstractFactoryPattern.ExtandAbstractFactory;namespace AbstractFactoryPattern
{internal class Program{static void Main(string[] args){InstallationEngineer2Test();Console.ReadLine();}/// <summary>/// 测试装机工程师2组装的电脑/// </summary>private static void InstallationEngineer2Test(){Console.WriteLine($"\n测试装机工程师组装电脑——使用抽象工厂模式");//创建装机工程师对象InstallationEngineer2 installationEngineer2 = new InstallationEngineer2();Console.WriteLine($"\n测试装机工程师组装电脑——使用方案一");//获取客户选择的装机方案一IInstallationSchema installationSchema = new InstallationSchema1();//客户告诉装机工程师自己选择的配件,让装机工程师组装installationEngineer2.InstallComputer(installationSchema);Console.WriteLine($"\n测试装机工程师组装电脑——使用方案二");//获取客户选择的装机方案一IInstallationSchema installationSchema2 = new InstallationSchema2();//客户告诉装机工程师自己选择的配件,让装机工程师组装installationEngineer2.InstallComputer(installationSchema2);}}//Class_end
}

运行结果如下:

        如上定义抽象工厂模式接口配合具体的方案是实现了配件间的相互匹配问题;但如果我们想要在当前的具体的产品簇(系列)里面新增一个配件(如:需要新增内存条)那么在现有的典型抽象工厂方法模式下就需要现在抽象工厂模式接口里面定义一个创建内存条的对象;然后还需要在所有继承了该抽象接口的具体类里面实现新增的内存条内容,这样就非常麻烦了,十分的不灵活。 

 2.3、使用可扩展抽象工厂方法模式的示例

        针对典型抽象工厂方法对于新增产品麻烦不灵活的问题,我们可以在编写可扩展的抽象工厂接口来解决这个问题(即:我们的整个抽象工厂接口里面不需要定义很多的方法,只是定义一个创建产品的方法,然后给这个方法设置一个参数,通过这个参数来判断具体创建什么产品对象)

1、创建可扩展的抽象工厂接口

/***
*	Title:"设计模式" 项目
*		主题:可拓展的抽象抽象工厂(不太安全)
*	Description:
*	    功能:抽象工厂模式的本质是【选择产品簇(系列)的实现】
*	        抽象工厂本质:是选择单个产品的实现,虽然一个类里面可以有多个工厂方法,但是这些方法之间一般是没有联系的;
*	                      抽象工厂着重的就是为一个产品簇选择实现;抽象工厂方法通常是有联系的,它们都是产品某一部分或是相互依赖的
*	        
*	        抽象工厂模式的优点:
*	                1、分离接口和实现(客户端使用抽象工厂来创建需要的对象,而客户端根本不知道具体的实现是谁,客户端只是面向产品的接口编程而已)
*	                2、使得切换产品系列变得容易(因为一个具体的工厂实现代表一个产品系列【如:Schema1代表装机方案一:Intel的CPU+技嘉的主板;
*	                     Schema2代表装机方案二:AMD的CPU+微星的主板;客户选用不同的模式,就相当于切换不同的产品系列】)
*	        
*	        抽象工厂模式的缺点:
*	                1、不太容易扩展新的产品(典型的抽象工厂方法若需要给整个产品新添加一个产品,那就需要修改抽象工厂,
*	                                        这样会导致修改所有的工厂实现类;可扩展的抽象工厂可以解决新增产品问题,但是又不够安全)
*	                2、容易造成类层次复杂(在使用抽象工厂模式时,若需要选择的层次过多,那么会造成整个类层次变得复杂)
*	       
*	       何时选用抽象工厂模式?
*	                1、如果希望一个系统独立于它的产品创建、组合和表示的时候【即:希望一个系统只知道产品的接口,而不关心实现】
*	                2、如果一个系统要由多个产品系列中的一个来配置的时候【即:可以动态的切换多个产品簇(系列)】
*	                3、如果要强调一系列相关产品的接口,以便联合使用它们的时候
*	    
*Date:2025
*	Version:0.1版本
*	Author:Coffee
*	Modify Recoder:***/using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.ExtandAbstractFactory
{/// <summary>/// 可拓展的抽象工厂接口/// </summary>internal interface IExtandAbstractFacoty{/// <summary>/// 一个通用的创建产品对象方法/// </summary>/// <param name="type">具体创建产品类型标识</param>/// <returns>返回被创建出的产品对象</returns>object CreateProduct(int type);}//Interface_end
}

2、编写一个类继承该可扩展的抽象工厂接口实现具体的各个配件匹配方案

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.ExtandAbstractFactory
{/// <summary>/// 装机方案一:Intel的CPU+技嘉的主板/// 该方案里面创建的CPU与主板是可以匹配对应上的/// </summary>internal class Schema1 : IExtandAbstractFacoty{/// <summary>/// 创建产品/// </summary>/// <param name="type">这里的类型是表示对应的产品对象(如:1表示CPU;2表示主板)</param>/// <returns></returns>public object CreateProduct(int type){object obj = null;//type为表四创建的产品类型(如:1表示CPU,2表示主板)switch (type){case 1:obj = new IntelCPU(1151);break;case 2:obj = new GAMainboard(1151);break;default:break;}return obj;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.ExtandAbstractFactory
{/// <summary>/// 装机方案二:AMD的CPU+微星的主板/// 该方案里面创建的CPU与主板是可以匹配对应上的/// </summary>internal class Schema2 : IExtandAbstractFacoty{/// <summary>/// 创建产品/// </summary>/// <param name="type">这里的类型是表示对应的产品对象(如:1表示CPU;2表示主板)</param>/// <returns></returns>public object CreateProduct(int type){object obj = null;switch (type){case 1:obj = new AmdCPU(940);break;case 2:obj = new MSIMainboard(940);break;default:break;}return obj;}}//Class_end
}

3、编写装机工程师类实现组装电脑功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.ExtandAbstractFactory
{internal class InstallationEngineer3{//定义组装电脑所需的CPU对象private ICPU cpu = null;//定义组装电脑所需的主板对象private IMainboard mainboard = null;public void InstallComputer(IExtandAbstractFacoty schema){//1、准备好装机所需的配件PrepareHardwares(schema);//2、组装电脑//3、测试电脑//4、交付客户}//准备装机所需的配件private void PrepareHardwares(IExtandAbstractFacoty schema){//这里使用方案获取需要的配件对象this.cpu = (ICPU)schema.CreateProduct(1);this.mainboard=(IMainboard)schema.CreateProduct(2);//测试配件是否正常this.mainboard.InstallCPU();this.cpu.Calculate();}}//Class_end
}

4、客户端使用该组装工程师类组装电脑

using AbstractFactoryPattern.AbstractFactory;
using AbstractFactoryPattern.DAO;
using AbstractFactoryPattern.ExtandAbstractFactory;namespace AbstractFactoryPattern
{internal class Program{static void Main(string[] args){InstallationEngineer3Test();Console.ReadLine();}/// <summary>/// 测试装机工程师3组装的电脑/// </summary>private static void InstallationEngineer3Test(){Console.WriteLine($"\n测试装机工程师组装电脑——使用拓展抽象工厂模式");//创建装机工程师对象InstallationEngineer3 installationEngineer3 = new InstallationEngineer3();Console.WriteLine($"\n使用客户选择的装机方案一【只有CPU与主板】");//获取客户选择的装机方案2IExtandAbstractFacoty installationSchema1 = new Schema1();//客户告诉装机工程师4自己选择的配件,让装机工程师组装(若此时没有对信息的memory判断就会报错)installationEngineer3.InstallComputer(installationSchema1);Console.WriteLine($"\n使用客户选择的装机方案二【只有CPU和主板");//获取客户选择的装机方案3IExtandAbstractFacoty installationSchema2 = new Schema2();//客户告诉装机工程师4自己选择的配件,让装机工程师组装installationEngineer3.InstallComputer(installationSchema2);}}//Class_end
}

运行结果如下:

如上是可扩展的抽象工厂的基本实现;从客户端的代码会发现,为什么说这种方式不安全呢?

【 是因为创建产品后返回的是object对象,但我们使用的时候需要转为具体的对象;如果此次返回的对象与我们需要转换的对象没有匹配上,但还是需要强制转为我们需要的对象,此时就会发生错误;就是这一点不太安全】。

5、我们接下来就使用扩展抽象方法模式来新增产品,体会以下可扩展抽象方法模式的灵活性

 《1》新增内存接口用来约束内存的功能行为

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// 内存接口/// </summary>internal interface IMemory{//内存具有缓存数据的能力,仅示意void CacheDatas();}//Interface_end
}

《2》编写一个闪迪类继承内存接口实现具体的功能行为

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern
{/// <summary>/// 闪迪内存条/// </summary>internal class SanDiskMemory:IMemory{//定义内存条的频率(HZ)private int memoryRate = 2133;public SanDiskMemory(){}public SanDiskMemory(int memoryRate){this.memoryRate = memoryRate;}public void CacheDatas(){Console.WriteLine($"现在使用闪迪内存条,内存条频率是【{memoryRate}】");}}//Class_end
}

《3》创建一个方案继承可扩展抽象工厂接口实现具体的内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.ExtandAbstractFactory
{internal class Schema3 : IExtandAbstractFacoty{/// <summary>/// 创建产品/// </summary>/// <param name="type">这里的类型是表示对应的产品对象(如:1表示CPU;2表示主板;3表示内存条)</param>/// <returns>返回具体的产品对象</returns>public object CreateProduct(int type){object obj = null;switch (type){case 1:obj = new AmdCPU(1151);break;case 2:obj = new MSIMainboard(1151);break;case 3:obj = new SanDiskMemory(3200);break;default:break;}return obj;}}//Class_end
}

《4》编写装机工程师类实现组装电脑

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AbstractFactoryPattern.ExtandAbstractFactory
{/// <summary>/// 装机工程师4/// </summary>internal class InstallationEngineer4{//定义组装电脑所需的CPU对象private ICPU cpu = null;//定义组装电脑所需的主板对象private IMainboard mainboard = null;//定义内存头条对象private IMemory memory = null;public void InstallComputer(IExtandAbstractFacoty schema){//1、准备好装机所需的配件PrepareHardwares(schema);//2、组装电脑//3、测试电脑//4、交付客户}//准备装机所需的配件private void PrepareHardwares(IExtandAbstractFacoty schema){//这里使用方案获取需要的配件对象this.cpu = (ICPU)schema.CreateProduct(1);this.mainboard=(IMainboard)schema.CreateProduct(2);//新增内存条对象this.memory=(SanDiskMemory)schema.CreateProduct(3);//测试配件是否正常this.mainboard.InstallCPU();this.cpu.Calculate();//新增内存条判断,这是因为前面的模式没有内存条,若不判断直接使用就会报错if (memory!=null){memory.CacheDatas();}}}//Class_end
}

注意:这里之所以需要在测试内存条功能的时候增加一个if判断,原因是为了要同时满足以前和现在的要求(以前的客户端,它调用的时候没有内存条对象,若直接调用这个最新的模式的到内存功能时就会报错;因此需要添加一个判断)。

《5》客户端调用工程师类实现组装电脑

using AbstractFactoryPattern.AbstractFactory;
using AbstractFactoryPattern.DAO;
using AbstractFactoryPattern.ExtandAbstractFactory;namespace AbstractFactoryPattern
{internal class Program{static void Main(string[] args){InstallationEngineer4Test();Console.ReadLine();}/// <summary>/// 测试装机工程师4组装的电脑/// </summary>private static void InstallationEngineer4Test(){Console.WriteLine($"\n测试装机工程师组装电脑——使用拓展抽象工厂模式");//创建装机工程师对象InstallationEngineer4 installationEngineer4 = new InstallationEngineer4();Console.WriteLine($"\n使用客户选择的装机方案二【只有CPU与主板】");//获取客户选择的装机方案2IExtandAbstractFacoty installationSchema2 = new Schema2();//客户告诉装机工程师4自己选择的配件,让装机工程师组装(若此时没有对信息的memory判断就会报错)installationEngineer4.InstallComputer(installationSchema2);Console.WriteLine($"\n使用客户选择的装机方案三【有CPU、主板和内存条】");//获取客户选择的装机方案3IExtandAbstractFacoty installationSchema3 = new Schema3();//客户告诉装机工程师4自己选择的配件,让装机工程师组装installationEngineer4.InstallComputer(installationSchema3);}}//Class_end
}

运行结果如下:

三、项目源码工程

kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern

相关文章:

学习设计模式《六》——抽象工厂方法模式

一、基础概念 抽象工厂模式的本质是【选择产品簇(系列)的实现】&#xff1b; 抽象工厂模式定义&#xff1a;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类&#xff1b; 抽象工厂模式功能&#xff1a;抽象工厂的功能是为一系列相关对象或相互依…...

MySQL 数据类型

文章目录 数据类型数据类型分类数据类型tinyint类型&#xff08;整型&#xff09;总结bit类型&#xff08;字节&#xff09; 浮点类型float类型decimal类型 字符串类型char类型varchar&#xff08;变长字符串&#xff09; char 和 varchar的对比日期类型enum和set类型&#xff…...

基于Tcp协议的应用层协议定制

前言&#xff1a;本文默认读者已掌握 TCP 协议相关网络接口知识&#xff0c;将聚焦于应用层协议的设计与剖析&#xff0c;有关底层通信机制及业务逻辑部分仅作简要概述&#xff0c;不再展开详述。 目录 服务器 一、通信 二、协议 1.序列化与反序列化 2. 封包与解包 三、业…...

Flink反压问题解析

一、什么是反压(Backpressure)? 反压(Backpressure) 是流处理系统中的一种流量控制机制。当下游算子处理速度低于上游数据生产速度时,系统会向上游传递压力信号,迫使上游降低数据发送速率,避免数据堆积和系统崩溃。 Flink 通过动态反压机制实现这一过程,但其副作用是…...

C语言中结构体的字节对齐的应用

一、字节对齐的基本原理 计算机的内存访问通常以固定大小的块&#xff08;如 4 字节、8 字节&#xff09;为单位。若数据的内存地址是块大小的整数倍&#xff0c;称为 自然对齐。例如&#xff1a; int&#xff08;4 字节&#xff09;的地址应为 4 的倍数。 double&#xff08…...

大规模数据同步后数据总条数对不上的系统性解决方案:从字段映射到全链路一致性保障

一、引言 在数据同步&#xff08;如系统重构、分库分表、多源整合&#xff09;场景中&#xff0c;“本地数据一致&#xff0c;生产环境条数对不上”是典型痛点。问题常源于并发处理失控、数据库性能瓶颈、字段映射错误、缓存脏数据等多维度缺陷。本文结合实战经验&#xff0c;…...

美团Java后端二面面经!

场景题是面试的大头&#xff0c;建议好好准备 Q. [美团]如何设计一个外卖订单的并发扣减库存系统&#xff1f; Q.[美团]为啥初始标记和重新标记需要STW&#xff1f; Q.[美团]骑手位置实时更新&#xff0c;如何保证高并发写入&#xff1f; Q.[美团]订单表数据量过大导致查询…...

35-疫苗预约管理系统(微服务)

技术&#xff1a; RuoYi框架 后端: SpringBootMySQLspringCloudnacosRedis 前端: vue3 环境&#xff1a; Idea mysql maven jdk1.8 用户端功能 1.首页:展示疫苗接种须知标语、快速预约模块 2.疫苗列表:展示可接种的疫苗 3.预约接种: 用户可进行疫苗预约接种 修改预约时间 …...

Ext JS模拟后端数据之SimManager

Ext.ux.ajax.SimManager 是 Ext JS 框架中用于拦截 Ajax 请求并返回模拟数据的核心工具,适用于前后端分离开发、原型验证或独立测试场景。它通过配置灵活的规则和模拟处理器(Simlet),帮助开发者在不依赖真实后端的情况下完成前端功能开发。 simlets 是simulated servers的…...

BT169-ASEMI无人机专用功率器件BT169

编辑&#xff1a;ll BT169-ASEMI无人机专用功率器件BT169 型号&#xff1a;BT169 品牌&#xff1a;ASEMI 封装&#xff1a;SOT-23 批号&#xff1a;最新 引脚数量&#xff1a;3 特性&#xff1a;单向可控硅 工作温度&#xff1a;-40℃~150℃ BT169单向可控硅&#xff…...

4月26日星期六今日早报简报微语报早读

4月26日星期六&#xff0c;农历三月廿九&#xff0c;早报#微语早读。 1、广州多条BRT相关线路将停运&#xff0c;全市BRT客运量较高峰时大幅下降&#xff1b; 2、国务院批复&#xff1a;同意在海南全岛等15地设立跨境电商综合试验区&#xff1b; 3、我国首次实现地月距离尺度…...

如何将 sNp 文件导入并绘制到 AEDT (HFSS)

导入 sNp 文件 打开您的项目&#xff0c;右键单击 “Result” 绘制结果 导入后&#xff0c;用户可以选择它进行打印。请参阅下面的示例。要点&#xff1a;确保从 Solution 中选择它。...

Shell脚本-for循环应用案例

在Shell脚本编程中&#xff0c;for循环是一种强大的工具&#xff0c;用于处理重复性任务。无论是批量处理文件、遍历目录内容还是简单的计数任务&#xff0c;for循环都能提供简洁而有效的解决方案。本文将通过几个实际的应用案例来展示如何使用for循环解决具体的编程问题。 案…...

MATLAB基础应用精讲-【基础知识篇】发布和共享 MATLAB 代码

目录 MATLAB发布代码---生成文档pdf 分节符对发布文件的分节 实时脚本 Matlab workspace与m脚本数据共享 发布和共享 MATLAB 代码 在实时编辑器中创建和共享实时脚本 发布 MATLAB 代码文件 (.m) 添加帮助和创建文档 发布 MATLAB 代码文件 (.m) 可创建包括您的代码、注释…...

Shell脚本-while循环语法结构

在Shell脚本编程中&#xff0c;while循环是一种重要的流程控制语句&#xff0c;它允许我们重复执行一段代码&#xff0c;直到指定的条件不再满足为止。与for循环不同&#xff0c;while循环通常用于条件驱动的迭代&#xff0c;而不是基于列表或范围的迭代。本文将详细介绍Shell脚…...

Java基础第四章、面向对象

一、成员变量 示例&#xff1a; 二、JVM内存模型 类变量就是静态变量 三、构造方法 默认构造方法、定义的构造方法(不含参数、含参数) 构造方法重载&#xff1a; this关键字 this关键字应用&#xff1a;对构造方法进行复用&#xff0c;必须放在第一行 四、面向对象的三大特征 1…...

【基础IO上】复习C语言文件接口 | 学习系统文件接口 | 认识文件描述符 | Linux系统下,一切皆文件 | 重定向原理

1.关于文件的预备知识 1.1 文件的宏观理解 广义上理解&#xff0c;键盘、显示器等都是文件&#xff0c;因为我们说过“Linux下&#xff0c;一切皆文件”&#xff0c;当然我们现在对于这句话的理解是片面的&#xff1b;狭义上理解&#xff0c;文件在磁盘上&#xff0c;磁盘是一…...

linux离线部署open-metadata

OpenMetadata 环境及离线资源关闭防火墙禁止防火墙关闭 SELinux 创建用户安装JDK安装mysql安装Elasticsearch安装open-metadata 环境及离线资源 系统&#xff1a;CentOS Linux release 7.9.2009 (Core) JDK&#xff1a;17 Mysql&#xff1a; 8.0 OpenMetadata&#xff1a;1.6.…...

Exposure Adjusted Incidence Rate (EAIR) 暴露调整发病率:精准量化疾病风险

1. 核心概念 1.1 传统发病率的局限性 1.1.1 公式与定义 传统发病率公式为新发病例数除以总人口数乘以观察时间。例如在某社区观察1年,有10例新发病例,总人口1000人,发病率即为10/10001=0.01。 此公式假设所有个体暴露时间和风险相同,但实际中个体差异大,如部分人暴露时间…...

信令与流程分析

WebRTC是h5支持的重要特征之一&#xff0c;有了它&#xff0c;不再需要借助音视频相关的客户端&#xff0c;直接通过浏览器的Web页面就可以实现音视频聊天功能。 WebRTC项目是开源的&#xff0c;我们可以借助WebRTC&#xff0c;构建自己的音视频聊缇娜功能。无论是前端JS的Web…...

声音分离人声和配乐base,vocals,drums -从头设计数字生命第6课, demucs——仙盟创梦IDE

demucs -n htdemucs --two-stemsvocals 未来之窗.mp3 demucs -n htdemucs --shifts5 之.mp3demucs -n htdemucs --shifts5 -o wlzcoutspl 未来之窗.mp3 伴奏提取人声分离技术具有多方面的重大意义&#xff0c;主要体现在以下几个领域&#xff1a; 音乐创作与制作 创作便利…...

Chrmo手动同步数据

地址栏输入 chrome://sync-internals分别点击这2个按钮即可触发手动同步...

【Dify系列教程重置精品版】第1课 相关概念介绍

文章目录 一、Dify是什么二、Dify有什么用三、如何玩转Dify?从螺丝刀到机甲战士的进阶指南官方网站:https://dify.ai github地址:https://github.com/langgenius/dify 一、Dify是什么 Dify(D​​efine + ​​I​​mplement + ​​F​​or ​​Y​​ou)。这是一款开源的大…...

【HTTP通信:生活中的邮局之旅】

HTTP通信&#xff1a;生活中的邮局之旅 HTTP通信就像是现代社会的邮政系统&#xff0c;让信息能够在互联网的城市间穿梭。下面我将用邮局比喻和图表来解释这个过程&#xff0c;以及它在现代应用中的重要性。 HTTP通信的旅程图解 #mermaid-svg-gC3zCsPpsFcq3sy3 {font-family:…...

Operating System 实验二 内存管理实验

目录 实验目标: 实验设备: 实验内容: (1)验证FIFO和Stack LRU页面置换算法 【代码(注释率不低于30%)】 【实验过程(截图)】 【结论】 (2)分别用FIFO和Stack LRU页置换算法,自己设定一个页面引用序列,绘制页错误次数和可用页帧总数的曲线并对比(可用Excel绘…...

深入解析YOLO v1:实时目标检测的开山之作

目录 YOLO v1 算法详解​ ​1. 核心思想​ ​2. 算法优势​ ​3. 网络结构&#xff08;Unified Detection&#xff09;​​ ​4. 关键创新​ ​5. 结构示意图&#xff08;Fig1&#xff09;​ Confidence Score 的计算​ 类别概率与 Bounding Box 的关系​ 后处理&…...

windows作业job介绍

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、作业job是什么&#xff1f;二、使用步骤1.代码示例 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; winapi网站&#xff1a; h…...

POLARIS土壤相关数据集

POLARIS相关数据集属于杜克大学&#xff08;Duke University&#xff09;土木与环境工程系&#xff08;CEE&#xff09;的水文学研究团队。该团队有三个总体主题&#xff1a;1&#xff09; 改善地球系统模型中地表异质性的表示&#xff0c;2&#xff09; 利用环境数据来描述在陆…...

【Harmony OS】组件

目录 组件概述 组件常用属性 系统内置组件 Text TextArea 多行文本输入框组件 TextInput 文本输入框 Button Image 图片组件&#xff0c;支持本地图片和网络图片 Radio 单选框 Checkbox 复选框 Blank 空白填充组件 Divider 分隔符 PatternLock 图案密码锁组件 Prog…...

找出字符串中第一个匹配项的下标

题目&#xff1a;28. 找出字符串中第一个匹配项的下标 给你两个字符串 haystack 和 needle&#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1。 …...

专家系统的知识获取、检测与组织管理——基于《人工智能原理与方法》的深度解析

前文我们已经了解了专家系统的基本概念和一般结构&#xff0c;系统中有专业的知识才是专家系统的关键&#xff0c;接下来对专家系统中的知识是如何获取、检测、组织和管理的进行探讨。 1.专家系统的基本概念&#xff1a;专家系统的基本概念解析——基于《人工智能原理与方法》…...

BUUCTF-[GWCTF 2019]re3

[GWCTF 2019]re3 查壳&#xff0c;64位无壳 然后进去发现主函数也比较简单&#xff0c;主要是一个长度校验&#xff0c;然后有一个mprotect函数&#xff0c;说明应该又是Smc&#xff0c;然后我们用脚本还原sub_402219函数处的代码 import idc addr0x00402219 size224 for …...

基准指数选股策略思路

一种基于Python和聚宽平台的量化交易策略&#xff0c;主要包含以下内容&#xff1a; 1. 导入必要的库 - 导入jqdata和jqfactor库用于数据获取和因子计算。 - 导入numpy和pandas库用于数据处理。 2. 初始化函数 - 设置基准指数为沪深300指数。 - 配置交易参数&#xff0c;如使用…...

【阿里云大模型高级工程师ACP习题集】2.5 优化RAG应用提升问答准确度(⭐️⭐️⭐️ 重点章节!!!)

习题集 【单选题】在RAG应用的文档解析与切片阶段,若遇到文档类型不统一,部分格式的文档不支持解析的问题,以下哪种解决方式不可行?( ) A. 开发对应格式的解析器 B. 转换文档格式 C. 直接忽略该类型文档 D. 改进现有解析器以支持更多格式 【多选题】在选择向量数据库时,…...

【torch\huggingface默认下载路径修改】.cache/torch/ 或 .cache/huggingface

问题 服务器的硬盘空间是有限的&#xff0c;系统上的固态硬盘空间又比较小&#xff0c;在跑深度学习模型的时候经常有默认下载权重的操作&#xff0c;不管是torch或者huggingface&#xff0c;如果不加管理&#xff0c;所有的权重都放在home/user/.cache 里面&#xff0c;迟早会…...

SpringBoot 常用注解大全

SpringBoot 常用注解大全 一、核心注解 1. 启动类注解 SpringBootApplication&#xff1a;组合注解&#xff0c;包含以下三个注解 Configuration&#xff1a;标记该类为配置类EnableAutoConfiguration&#xff1a;启用自动配置ComponentScan&#xff1a;组件扫描 2. 配置相…...

【器件专题1——IGBT第2讲】IGBT 基本工作原理:从结构到特性,一文解析 “电力电子心脏” 的核心机制

IGBT&#xff08;绝缘栅双极型晶体管&#xff0c;Insulated Gate Bipolar Transistor&#xff09;作为现代电力电子领域的核心器件&#xff0c;其工作原理融合了 MOSFET 的高效控制优势与 BJT 的大功率处理能力。本文从物理结构、导通 / 关断机制、核心特性等维度&#xff0c;深…...

再谈String

1、字符串常量池 1.1 创建对象的思考 下面是两种创建字符串对象的代码 public static void main1(String[] args) {String s1 "hello";String s2 "hello";System.out.println(s1 s2);//trueString s3 new String("hello");String s4 new …...

语音合成之五语音合成中的“一对多”问题主流模型解决方案分析

语音合成中的“一对多”问题主流模型解决方案分析 引言“一对多”指的是什么&#xff1f;优秀开源模型的方法CosyvoiceSparkTTSLlaSA TTSVITS 引言 TTS系统旨在模仿人类的自然语音&#xff0c;但其核心面临着一个固有的挑战&#xff0c;即“一对多”问题 。这意味着对于给定的…...

嵌入式:Linux系统应用程序(APP)启动参数及其规则详解

在 systemd 的服务单元文件中&#xff0c;[Service] 部分用于定义服务的启动、停止、重启等操作&#xff0c;以及服务的运行环境和参数。以下是 [Service] 部分常见参数及其规则的详细介绍&#xff1a; 服务类型相关参数 **Type** **作用**&#xff1a;指定服务的启动类型&…...

25%甘油(灭菌)保存菌液以及10%甘油(普通)保存蛋白的原理及操作-实验操作系列-010

01 甘油保菌实验原理 1. 渗透压调节 甘油作为渗透压调节剂&#xff0c;能显著降低水的结冰温度&#xff0c;防止低温环境中细菌细胞内冰晶的形成。冰晶会破坏细胞膜&#xff0c;从而损伤细胞的完整性。甘油能够减少冰晶的生成&#xff0c;维持细胞结构的稳定&#xff0c;保护…...

影楼精修-手部青筋祛除算法解析

注意&#xff1a;本文样例图片为了避免侵权&#xff0c;均使用AIGC生成&#xff1b; 手部青筋祛除科普 手部青筋祛除是影楼精修中一个非常精细的工作&#xff0c;需要较高的修图技巧&#xff0c;目前市面上很少有自动化的青筋祛除功能的&#xff0c;而像素蛋糕目测是第一个做到…...

【时时三省】Python 语言----函数

山不在高,有仙则名。水不在深,有龙则灵。 ----CSDN 时时三省 1,函数概念 为了实现某种功能而组织的语句集合 定义格式: def 函数名([参数])函数体def add_num(a,b):c = a + bprint(c)add_num(11, 22) def 是定义函数的关键字。定义函数时,需要注意以下问题: 1,不需要说…...

蜜罐管理和数据收集服务器:Modern Honey Network (MHN)

一、Modern Honey Network (MHN)介绍 Modern Honey Network (MHN) 是一个集中化的蜜罐管理和数据收集服务器。它旨在简化蜜罐的部署和管理&#xff0c;并提供一个简洁的 Web 界面来查看捕获的数据。 1、主要功能: 集中化管理: 通过一个中心服务器管理多个蜜罐传感器。快速部…...

关于hbaseRegion和hbaseRowKey的一些处理

我遇到了什么问题&#xff1f; 我的habse一共有三台服务器&#xff0c;其中一台忙的要死&#xff0c;另外两台吃瓜看戏&#xff0c;我的业务都在其中一个服务器上&#xff0c;导致数据的读写瓶颈。 先说一下hbase的概况&#xff0c;有一个整体了解&#xff0c;我们再讲原因。…...

exec和spawn

前言 需求&#xff1a;做一个electron应用&#xff0c;用node打开exe软件&#xff0c;打开后返回成功与否&#xff0c;打开的软件不会随electron应用的关闭而关闭 exec exec 第一个参数为要运行的command命令&#xff0c;参数以空格分隔。 child_process.exec(command[, opti…...

【C到Java的深度跃迁:从指针到对象,从过程到生态】第四模块·Java特性专精 —— 第十三章 异常处理:超越C错误码的文明时代

一、错误处理的范式革命 1.1 C错误处理的黑暗时代 C语言通过返回值传递错误状态&#xff0c;存在系统性缺陷&#xff1a; 典型错误处理模式&#xff1a; FILE* open_file(const char* path) { FILE* f fopen(path, "r"); if (!f) { return NULL; // 错误信息…...

AD相同网络的铜皮和导线连接不上

出现这样的情况是不是很烦恼&#xff0c;明明是相同的网络连接不上&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 直接修改铜皮属性&#xff08;选择所有相同这个选项&#xff09; 这样就可以连接上了...

驱动开发硬核特训 · Day 21(下篇): 深入剖析 PCA9450 驱动如何接入 regulator 子系统

&#x1f4d8; 一、设备树视角&#xff1a;PCA9450 是如何声明的&#xff1f; 设备树中定义了 PCA9450 芯片通过 I2C 总线挂载&#xff0c;并描述了多个 regulator 通道&#xff1a; &i2c1 {pmic25 {compatible "nxp,pca9450c";reg <0x25>;regulators …...

消息队列mq在Mlivus Cloud向量数据库中的关键配置与最佳实践

作为《向量数据库指南》的作者和大禹智库高级研究员,我在30多年的向量数据库实战中深刻认识到:消息队列(MQ)作为现代向量数据库架构的"神经系统",其配置优化直接决定了系统的吞吐量、稳定性和扩展性。本文将基于Mlivus Cloud这一领先的向量数据库平台,深入剖析…...