学习设计模式《六》——抽象工厂方法模式
一、基础概念
抽象工厂模式的本质是【选择产品簇(系列)的实现】;
抽象工厂模式定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类;
抽象工厂模式功能:抽象工厂的功能是为一系列相关对象或相互依赖对象创建一个接口【抽象工厂其实是一个产品簇(系列)】【特别注意:这个接口内的方法不是任意堆砌的,而是一系列相关或相互依赖的方法】;
抽象工厂通常实现为接口;
切换产品簇(系列):由于抽象工厂定义的一系列对象通常是相关或相互依赖的,这些产品构成来了一个系列或产品簇; 这就带来了很大的灵活性,即切换一个产品簇的时候,只要提供不同的抽象工厂实现就可以。
序号 | 抽象工厂方法模式优点 | 抽象工厂方法模式缺点 |
1 | 分离接口和实现 (客户端使用抽象工厂来创建需要的对象,而客户端根本不知道具体的实现是谁,客户端只是面向产品的接口编程而已) | 不太容易扩展新的产品 (典型的抽象工厂方法若需要给整个产品新添加一个产品,那就需要修改抽象工厂,这样会导致修改所有的工厂实现类;可扩展的抽象工厂可以解决新增产品问题,但是又不够安全) |
2 | 使得切换产品系列变得容易 (因为一个具体的工厂实现代表一个产品系列【如:Schema1代表装机方案一:Intel的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
相关文章:

学习设计模式《六》——抽象工厂方法模式
一、基础概念 抽象工厂模式的本质是【选择产品簇(系列)的实现】; 抽象工厂模式定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类; 抽象工厂模式功能:抽象工厂的功能是为一系列相关对象或相互依…...

python_BeautifulSoup提取html中的信息
目录 描述: 过程: step one 下载html网页到本地 step two 提取html信息 list_con soup.select(.list-con) [0] li_list list_con.find_all(li) a li.find(span).find(a) title a.get(title) url a.get(href) span li.find(span).find(spa…...
单例设计模式之懒汉式以及线程安全问题
在单例设计模式中,懒汉式(Lazy Initialization) 通过延迟实例化来优化资源使用,但在多线程环境下存在线程安全问题。以下是其核心问题及解决方案的详细解析: 一、基础懒汉式代码(线程不安全) pu…...

今日头条如何查看IP归属地?详细教程与常见问题解答
在当今互联网时代,IP属地信息已成为各大社交平台展示用户真实性的重要标识。今日头条作为国内领先的资讯平台,也提供了IP属地显示功能。那么,今日头条怎么查看IP归属地?本文将详细介绍在今日头条11.9.0版本中如何查看自己和他人的…...
React-Hook
一、基础 Hooks 1、useState - 状态管理 useState 是 React 提供的一个函数,用来在函数组件中声明和修改状态,没有它,函数组件只是一个“静态模板”;有了它,函数组件可以保存和更新数据(比如计数器数值、输…...
前端节流、防抖函数
节流 什么是节流? 节流就是同一个事件 一秒钟他执行了很多次。但是我不想他执行这么多次,我只想让他执行一次 或者两次。 那该怎么办? why baby why 那我想就是他执行的时候 我就设置一个定时器,如果定时器是空的,等会…...
高级java每日一道面试题-2025年4月26日-基础篇[反射篇]-什么是类型擦除?它与反射之间有什么关系?
如果有遗漏,评论区告诉我进行补充 面试官: 什么是类型擦除?它与反射之间有什么关系? 我回答: 类型擦除与反射的深度解析 一、类型擦除(Type Erasure) 类型擦除是Java泛型实现的核心机制,旨在通过编译期处理确保向…...
Centos7系统防火墙使用教程
CentOS 7是一种常见的Linux操作系统,防火墙作为网络安全的第一道防线,对于服务器的安全至关重要。本文将介绍CentOS 7系统中防火墙的使用教程,包括如何开启、关闭、配置以及防火墙规则的添加和删除。 一、查看防火墙状态 在开始操作之前&am…...
缓存与数据库数据一致性:旁路缓存、读写穿透和异步写入模式解析
旁路缓存模式、读写穿透模式和异步缓存写入模式是三种常见的缓存使用模式,以下是对三种经典缓存使用模式在缓存与数据库数据一致性方面更全面的分析: 一、旁路缓存模式(Cache - Aside Pattern) 1.数据读取流程 应用程序首先向缓…...

【物联网】基于LORA组网的远程环境监测系统设计(机智云版)
基于LORA组网的远程环境监测系统设计(机智云版) 演示视频: 简介: 1.本系统有一个主机,两个从机。 2.一主多从的LORA组网通信,主机和两个从机都配备了STM32F103单片机与 LoRa 模块,主机作为中心设备及WIFI网关,负责接收和发送数据到远程物联网平台和手机APP,两个从机…...
Pygame事件处理详解:键盘、鼠标与自定义事件
Pygame事件处理详解:键盘、鼠标与自定义事件 在游戏开发中,玩家的交互是至关重要的。无论是移动角色、触发动作还是暂停游戏,都需要通过各种输入来实现。Pygame作为一个功能强大的Python库,提供了丰富的API来处理这些输入,包括键盘、鼠标以及自定义事件。本文将详细介绍如…...

制作一款打飞机游戏22:表格导出
编辑器功能扩展 今天,我想让编辑器能够处理一个数组,这是编辑器将要编辑的东西,它只编辑数组。这些区域在后续的不同版本的编辑器中会有不同的含义,但现在我想创建一个模板,能够加载一个二维数组,并将二维…...

Linux内核源码结构
目录 Linux内核源码结构 Linux内核版本命名 Linux内核版本选择 内核源码结构 arch:与CPU架构相关的源代码 block:磁盘设备的支持 COPYING文件 CREDITS文件 crypto:加密相关 Documentation: drivers:设备驱动 firmware:固件 fs:文件系统 include:头文件…...

72.评论日记
【巫师】中美关税战02:应给人民爆装备,以及普通人如何应对(7条建议)_哔哩哔哩_bilibili 2025年4月26日11:03:31...
在springboot项目中,如何进行excel表格的导入导出功能?
以下是使用 Apache POI 和 EasyExcel 实现 Excel 表格导入导出功能的具体代码示例。 1. 使用 Apache POI 实现 Excel 导入导出 添加依赖 在 pom.xml 中添加 Apache POI 的依赖: <dependency><groupId>org.apache.poi</groupId><artifactId…...

Websocket自动发送消息客户端工具
点击下载《Websocket自动发送消息客户端工具》 1. 前言 在现代网络应用中,实时通信和即时数据传输变得越来越重要。WebSocket作为一种全双工通信协议,因其高效、实时的特点,被广泛应用于聊天应用、实时数据监控、在线游戏等领域。然而&…...

STM32的开发环境介绍
目录 STM32软件环境 Keil软件在线安装 其他软件环境安装 STM32开发的几种方式 STM32寄存器版本和库函数版本 标准外设库的作用: STM32软件环境 STM32 的集成开发环境(IDE):编辑编译软件 常见的环境: (1)KEIL&a…...

数据库系统概论(四)关系操作,关系完整性与关系代数
数据库系统概论(四)详细讲解关系操作,关系完整性与关系代数 前言一、什么是关系操作1.1 基本的关系操作1.2 关系数据语言的分类有哪些 二、关系的完整性2.1 实体完整性2.2 参照完整性2.3 用户的定义完整性 三、关系代数是什么3.1 传统的集合运…...

基于 IPMI + Kickstart + Jenkins 的 OS 自动化安装
Author:Arsen Date:2025/04/26 目录 环境要求实现步骤自定义 ISO安装 ipmitool安装 NFS定义 ks.cfg安装 HTTP编写 Pipeline 功能验证 环境要求 目标服务器支持 IPMI / Redfish 远程管理(如 DELL iDRAC、HPE iLO、华为 iBMC)&…...
【AI提示词】财务顾问
提示说明 财务顾问是一个专注于帮助个人和企业优化财务状况、制定财务计划并实现财务目标的专业人士。 提示词 # Role: 财务顾问## Profile - language: 中文 - description: 财务顾问是一个专注于帮助个人和企业优化财务状况、制定财务计划并实现财务目标的专业人士。 - ba…...

使用 Node、Express 和 MongoDB 构建一个项目工程
本文将详细介绍如何使用 Node.js Express MongoDB 构建一个完整的 RESTful API 后端项目,涵盖: 项目初始化 Express 服务器搭建 MongoDB 数据库连接 REST API 设计(CRUD 操作) 错误处理与中间件 源码结构与完整代码 部署建…...

【C++11】右值引用和移动语义:万字总结
📝前言: 这篇文章我们来讲讲右值引用和移动语义 🎬个人简介:努力学习ing 📋个人专栏:C学习笔记 🎀CSDN主页 愚润求学 🌄其他专栏:C语言入门基础,python入门基…...
【滑动窗口+哈希表/数组记录】Leetcode 3. 无重复字符的最长子串
题目要求 给定一个字符串 s,找出其中不含有重复字符的最长子串的长度。 子字符串是字符串中连续非空字符序列。 示例 1 输入:s "abcabcbb" 输出:3 解释:无重复字符的最长子串是 "abc",长度为…...
pytest 技术总结
目录 一 pytest的安装: 二 pytest有三种启动方式: 三 用例规则: 四 配置框架: 一 pytest的安装: pip install pytest # 安装 pip install pytest -U # 升级到最新版 二 pytest有三种启动方式: 1…...
java中的Selector详解
Selector(选择器)是Java NIO(非阻塞I/O)的核心组件,用于实现I/O多路复用,允许单个线程管理多个通道(Channel),从而高效处理高并发场景。 一、Selector的核心概念与作用 I/O多路复用 Selector通过事件驱动机制,监听多个通道的就绪状态(如可读、可写、连接建立等),无…...
DeepSeek 的长上下文扩展机制
DeepSeek 在基础预训练完成后,引入 YaRN(Yet another RoPE extensioN method)技术,通过额外的训练阶段将模型的上下文窗口从默认的 4K 逐步扩展至 128K。整个过程分为两个阶段:第一阶段将上下文窗口从 4K 扩展到 32K;第二阶段则进一步从 32K 扩展到 128K。每个阶段均采用…...
【修复】Django收到请求报Json解析错误
Django收到请求报Json解析错误 场景分析解决 场景 在使用Postman发送Django的请求时,只能使用原来的json内容,如果修改json内容则会报json解析上的错误 分析 可能是有对请求内容的长度做了上报校验 解决 最终在请求头Headers里找到了Content-Length…...
openEuler对比CentOS的核心优势分析
openEuler对比CentOS的核心优势分析 在开源操作系统领域,openEuler与CentOS均占据重要地位,但随着CentOS维护策略的调整(如CentOS 8停止维护,转向CentOS Stream),越来越多的用户开始关注国产化替代方案。o…...

Python基于Django的全国二手房可视化分析系统【附源码】
博主介绍:✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇&…...

VulnHub-DC-2靶机渗透教程
VulnHub-DC-2靶机渗透教程 1.靶机部署 [Onepanda] Mik1ysomething 靶机下载:https://download.vulnhub.com/dc/DC-2.zip 直接使用VMware导入打开就行 2.信息收集 2.1 获取靶机ip(arp-scan/nmap) arp-scan -l nmap 192.168.135.0/24 2.2 详细信息扫描(nmap)…...