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

news2025/4/28 13:00:25

一、基础概念

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

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

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

抽象工厂方法模式优缺点
序号抽象工厂方法模式优点抽象工厂方法模式缺点
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
    {
        //定义装机需要的CPU
        private 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
    {
        //定义获取组装电脑所需的CPU
        private 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与主板】");
            //获取客户选择的装机方案2
            IExtandAbstractFacoty installationSchema1 = new Schema1();
            //客户告诉装机工程师4自己选择的配件,让装机工程师组装(若此时没有对信息的memory判断就会报错)
            installationEngineer3.InstallComputer(installationSchema1);

            Console.WriteLine($"\n使用客户选择的装机方案二【只有CPU和主板");
            //获取客户选择的装机方案3
            IExtandAbstractFacoty 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与主板】");
            //获取客户选择的装机方案2
            IExtandAbstractFacoty installationSchema2 = new Schema2();
            //客户告诉装机工程师4自己选择的配件,让装机工程师组装(若此时没有对信息的memory判断就会报错)
            installationEngineer4.InstallComputer(installationSchema2);

            Console.WriteLine($"\n使用客户选择的装机方案三【有CPU、主板和内存条】");
            //获取客户选择的装机方案3
            IExtandAbstractFacoty installationSchema3 = new Schema3();
            //客户告诉装机工程师4自己选择的配件,让装机工程师组装
            installationEngineer4.InstallComputer(installationSchema3);
        }

    }//Class_end
}

运行结果如下:

三、项目源码工程

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

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

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

相关文章

python_BeautifulSoup提取html中的信息

目录 描述&#xff1a; 过程&#xff1a; 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…

今日头条如何查看IP归属地?详细教程与常见问题解答

在当今互联网时代&#xff0c;IP属地信息已成为各大社交平台展示用户真实性的重要标识。今日头条作为国内领先的资讯平台&#xff0c;也提供了IP属地显示功能。那么&#xff0c;今日头条怎么查看IP归属地&#xff1f;本文将详细介绍在今日头条11.9.0版本中如何查看自己和他人的…

【物联网】基于LORA组网的远程环境监测系统设计(机智云版)

基于LORA组网的远程环境监测系统设计(机智云版) 演示视频: 简介: 1.本系统有一个主机,两个从机。 2.一主多从的LORA组网通信,主机和两个从机都配备了STM32F103单片机与 LoRa 模块,主机作为中心设备及WIFI网关,负责接收和发送数据到远程物联网平台和手机APP,两个从机…

制作一款打飞机游戏22:表格导出

编辑器功能扩展 今天&#xff0c;我想让编辑器能够处理一个数组&#xff0c;这是编辑器将要编辑的东西&#xff0c;它只编辑数组。这些区域在后续的不同版本的编辑器中会有不同的含义&#xff0c;但现在我想创建一个模板&#xff0c;能够加载一个二维数组&#xff0c;并将二维…

Linux内核源码结构

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

72.评论日记

【巫师】中美关税战02&#xff1a;应给人民爆装备&#xff0c;以及普通人如何应对(7条建议)_哔哩哔哩_bilibili 2025年4月26日11:03:31

Websocket自动发送消息客户端工具

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

STM32的开发环境介绍

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

数据库系统概论(四)关系操作,关系完整性与关系代数

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

基于 IPMI + Kickstart + Jenkins 的 OS 自动化安装

Author&#xff1a;Arsen Date&#xff1a;2025/04/26 目录 环境要求实现步骤自定义 ISO安装 ipmitool安装 NFS定义 ks.cfg安装 HTTP编写 Pipeline 功能验证 环境要求 目标服务器支持 IPMI / Redfish 远程管理&#xff08;如 DELL iDRAC、HPE iLO、华为 iBMC&#xff09;&…

使用 Node、Express 和 MongoDB 构建一个项目工程

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

【C++11】右值引用和移动语义:万字总结

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲右值引用和移动语义 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C学习笔记 &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&#xff1a;C语言入门基础&#xff0c;python入门基…

Python基于Django的全国二手房可视化分析系统【附源码】

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

VulnHub-DC-2靶机渗透教程

VulnHub-DC-2靶机渗透教程 1.靶机部署 [Onepanda] Mik1ysomething 靶机下载&#xff1a;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)…

n8n 中文系列教程_10. 解析n8n中的AI节点:从基础使用到高级Agent开发

在自动化工作流中集成AI能力已成为提升效率的关键。n8n通过内置的LangChain节点&#xff0c;让开发者无需复杂代码即可快速接入GPT-4、Claude等大模型&#xff0c;实现文本处理、智能决策等高级功能。本文将深入解析n8n的AI节点体系&#xff0c;从基础的Basic LLM Chain到强大的…

计算机网络 | 应用层(1)--应用层协议原理

&#x1f493;个人主页&#xff1a;mooridy &#x1f493;专栏地址&#xff1a;《计算机网络&#xff1a;自定向下方法》 大纲式阅读笔记 关注我&#x1f339;&#xff0c;和我一起学习更多计算机的知识 &#x1f51d;&#x1f51d;&#x1f51d; 目录 1. 应用层协议原理 1.1 …

MuJoCo 关节角速度记录与可视化,监控机械臂运动状态

视频讲解&#xff1a; MuJoCo 关节角速度记录与可视化&#xff0c;监控机械臂运动状态 代码仓库&#xff1a;GitHub - LitchiCheng/mujoco-learning 关节空间的轨迹优化&#xff0c;实际上是对于角速度起到加减速规划的控制&#xff0c;故一般来说具有该效果的速度变化会显得丝…

LVGL模拟器:NXP GUIDER+VSCODE

1. 下载安装包 NXP GUIDER&#xff1a;GUI Guider | NXP 半导体 CMAKE&#xff1a;Download CMake MINGW&#xff1a;https://github.com/niXman/mingw-builds-binaries/releases SDL2&#xff1a;https://github.com/libsdl-org/SDL/releases/tag/release-2.30.8 VSCODE&…

《USB技术应用与开发》第四讲:实现USB鼠标

一、标准鼠标分析 1.1简介 1.2页面显示 其中页面显示的“”不用管它&#xff0c;因为鼠标作为物理抓包&#xff0c;里面有时候会抓到一些错误&#xff0c;不一定是真正的通讯错误&#xff0c;很可能是本身线路接触质量不好等原因才打印出来的“”。 1.3按下鼠标左键 &#x…

一、鸿蒙编译篇

一、下载源码和编译 https://blog.csdn.net/xusiwei1236/article/details/142675221 https://blog.csdn.net/xiaolizibie/article/details/146375750 https://forums.openharmony.cn/forum.php?modviewthread&tid897 repo init -u https://gitee.com/openharmony/mani…