.NET设计模式(5):工厂方法模式(Factory Method)

简介:

工厂方法模式(Factory Method

——.NET设计模式系列之五
Terrylee 200412
概述
在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口。如何应对这种变化?提供一种封装机制来隔离出“这个易变对象”的变化,从而保持系统中“其它依赖该对象的对象”不随着需求的改变而改变?这就是要说的Factory Method模式了。
意图
定义一个用户创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
结构图
生活中的例子
工厂方法定义一个用于创建对象的接口,但是让子类决定实例化哪个类。压注成型演示了这种模式。塑料玩具制造商加工塑料粉,将塑料注入到希望形状的模具中。玩具的类别(车,人物等等)是由模具决定的。  
工厂方法解说
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不接触哪一个产品类被实例化这种细节。这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。在 Factory Method 模式中,工厂类与产品类往往具有平行的等级结构,它们之间一一对应。
现在我们考虑一个日志记录的例子(这里我们只是为了说明 Factory Method 模式,实际项目中的日志记录不会这么去做,也要比这复杂一些)。假定我们要设计日志记录的类,支持记录的方法有 FileLog EventLog 两种方式。在这里我们先不谈设计模式,那么这个日志记录的类就很好实现了:
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// 日志记录类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  Log
 5 ExpandedBlockStart.gif     {
 6InBlock.gif
 7InBlock.gif        public void WriteEvent()
 8ExpandedSubBlockStart.gif        {
 9InBlock.gif            Console.WriteLine("EventLog Success!");
10ExpandedSubBlockEnd.gif        }

11InBlock.gif    
12InBlock.gif        public void WriteFile()
13ExpandedSubBlockStart.gif        {
14InBlock.gif            Console.WriteLine("FileLog Success!");
15ExpandedSubBlockEnd.gif        }

16InBlock.gif
17InBlock.gif        public void Write(string LogType)
18ExpandedSubBlockStart.gif        {
19InBlock.gif            switch(LogType.ToLower())
20ExpandedSubBlockStart.gif            {
21InBlock.gif                case "event":
22InBlock.gif                    WriteEvent();
23InBlock.gif                    break;
24InBlock.gif
25InBlock.gif                case "file":
26InBlock.gif                    WriteFile();
27InBlock.gif                    break;
28InBlock.gif
29InBlock.gif                default:
30InBlock.gif                    break;
31ExpandedSubBlockEnd.gif            }

32ExpandedSubBlockEnd.gif        }

33ExpandedBlockEnd.gif    }

34 None.gif
这样的程序结构显然不能符合我们的要求,如果我们增加一种新的日志记录的方式 DatabaseLog ,那就要修改 Log 类,随着记录方式的变化, switch 语句在不断的变化,这样就引起了整个应用程序的不稳定,进一步分析上面的代码,发现对于 EventLog FileLog 是两种完全不同的记录方式,它们之间不应该存在必然的联系,而应该把它们分别作为单独的对象来对待。
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// EventLog类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  EventLog
 5 ExpandedBlockStart.gif {
 6InBlock.gif    public void Write()
 7ExpandedSubBlockStart.gif    {
 8InBlock.gif        Console.WriteLine("EventLog Write Success!");
 9ExpandedSubBlockEnd.gif    }

10ExpandedBlockEnd.gif}

11 None.gif
12 ExpandedBlockStart.gif /// <summary>
13InBlock.gif/// FileLog类
14ExpandedBlockEnd.gif/// </summary>

15 None.gif public   class  FileLog
16 ExpandedBlockStart.gif {
17InBlock.gif    public void Write()
18ExpandedSubBlockStart.gif    {
19InBlock.gif        Console.WriteLine("FileLog Write Success!");
20ExpandedSubBlockEnd.gif    }

21ExpandedBlockEnd.gif}

22 None.gif
进一步抽象,为它们抽象出一个共同的父类,结构图如下:
实现代码:
1 ExpandedBlockStart.gif /// <summary>
2InBlock.gif/// Log类
3ExpandedBlockEnd.gif/// </summary>

4 None.gif public   abstract   class  Log
5 ExpandedBlockStart.gif {
6InBlock.gif    public abstract void Write();
7ExpandedBlockEnd.gif}

8 None.gif
此时EventLog FileLog类的代码应该如下:
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// EventLog类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  EventLog:Log
 5 ExpandedBlockStart.gif {
 6InBlock.gif    public override void Write()
 7ExpandedSubBlockStart.gif    {
 8InBlock.gif        Console.WriteLine("EventLog Write Success!");
 9ExpandedSubBlockEnd.gif    }

10ExpandedBlockEnd.gif}

11 ExpandedBlockStart.gif /// <summary>
12InBlock.gif/// FileLog类
13ExpandedBlockEnd.gif/// </summary>

14 None.gif public   class  FileLog:Log
15 ExpandedBlockStart.gif {
16InBlock.gif    public override void Write()
17ExpandedSubBlockStart.gif    {
18InBlock.gif        Console.WriteLine("FileLog Write Success!");
19ExpandedSubBlockEnd.gif    }

20ExpandedBlockEnd.gif}

21 None.gif
此时我们再看增加新的记录日志方式 DatabaseLog 的时候,需要做哪些事情?只需要增加一个继承父类 Log 的子类来实现,而无需再去修改 EventLog FileLog 类,这样的设计满足了类之间的层次关系,又很好的符合了面向对象设计中的单一职责原则,每一个类都只负责一件具体的事情。到这里似乎我们的设计很完美了,事实上我们还没有看客户程序如何去调用。   在应用程序中,我们要使用某一种日志记录方式,也许会用到如下这样的语句:
None.gif EventLog eventlog  =   new  EventLog();
None.gifeventlog.Write();
None.gif
当日志记录的方式从 EventLog 变化为 FileLog ,我们就得修改所有程序代码中出现上面语句的部分,这样的工作量是可想而知的。此时就需要解耦具体的日志记录方式和应用程序。这就要引入 Factory Method 模式了,每一个日志记录的对象就是工厂所生成的产品,既然有两种记录方式,那就需要两个不同的工厂去生产了,代码如下:
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// EventFactory类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  EventFactory
 5 ExpandedBlockStart.gif {
 6InBlock.gif    public EventLog Create()
 7ExpandedSubBlockStart.gif    {
 8InBlock.gif        return new EventLog();
 9ExpandedSubBlockEnd.gif    }

10ExpandedBlockEnd.gif}

11 ExpandedBlockStart.gif /// <summary>
12InBlock.gif/// FileFactory类
13ExpandedBlockEnd.gif/// </summary>

14 None.gif public   class  FileFactory
15 ExpandedBlockStart.gif {
16InBlock.gif    public FileLog Create()
17ExpandedSubBlockStart.gif    {
18InBlock.gif        return new FileLog();
19ExpandedSubBlockEnd.gif    }

20ExpandedBlockEnd.gif}

21 None.gif
这两个工厂和具体的产品之间是平行的结构,并一一对应,并在它们的基础上抽象出一个公用的接口,结构图如下:
实现代码如下:
1 ExpandedBlockStart.gif /// <summary>
2InBlock.gif/// LogFactory类
3ExpandedBlockEnd.gif/// </summary>

4 None.gif public   abstract   class  LogFactory
5 ExpandedBlockStart.gif {
6InBlock.gif    public abstract Log Create();
7ExpandedBlockEnd.gif}

8 None.gif
此时两个具体工厂的代码应该如下:
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// EventFactory类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  EventFactory:LogFactory
 5 ExpandedBlockStart.gif {
 6InBlock.gif    public override EventLog Create()
 7ExpandedSubBlockStart.gif    {
 8InBlock.gif        return new EventLog();
 9ExpandedSubBlockEnd.gif    }

10ExpandedBlockEnd.gif}

11 ExpandedBlockStart.gif /// <summary>
12InBlock.gif/// FileFactory类
13ExpandedBlockEnd.gif/// </summary>

14 None.gif public   class  FileFactory:LogFactory
15 ExpandedBlockStart.gif {
16InBlock.gif    public override FileLog Create()
17ExpandedSubBlockStart.gif    {
18InBlock.gif        return new FileLog();
19ExpandedSubBlockEnd.gif    }

20ExpandedBlockEnd.gif}

21 None.gif
这样通过工厂方法模式我们把上面那对象创建工作封装在了工厂中,此时我们似乎完成了整个 Factory Method 的过程。这样达到了我们应用程序和具体日志记录对象之间解耦的目的了吗?看一下此时客户端程序代码:
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// App类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  App
 5 ExpandedBlockStart.gif {
 6InBlock.gif    public static void Main(string[] args)
 7ExpandedSubBlockStart.gif    {
 8InBlock.gif        LogFactory factory = new EventFactory();
 9InBlock.gif
10InBlock.gif        Log log = factory.Create();
11InBlock.gif
12InBlock.gif        log.Write();
13ExpandedSubBlockEnd.gif    }

14ExpandedBlockEnd.gif}

15 None.gif
在客户程序中,我们有效地避免了具体产品对象和应用程序之间的耦合,可是我们也看到,增加了具体工厂对象和应用程序之间的耦合。那这样究竟带来什么好处呢?我们知道,在应用程序中, Log 对象的创建是频繁的,在这里我们可以把
LogFactory factory = new EventFactory();
这句话放在一个类模块中,任何需要用到 Log 对象的地方仍然不变。要是换一种日志记录方式,只要修改一处为:
LogFactory factory = new FileFactory();
其余的任何地方我们都不需要去修改。有人会说那还是修改代码,其实在开发中我们很难避免修改,但是我们可以尽量做到只修改一处。
其实利用 .NET 的特性,我们可以避免这种不必要的修改。下面我们利用 .NET 中的反射机制来进一步修改我们的程序,这时就要用到配置文件了,如果我们想使用哪一种日志记录方式,则在相应的配置文件中设置如下:
1 None.gif < appSettings >
2 None.gif     < add  key ="factoryName"  value ="EventFactory" ></ add >
3 None.gif </ appSettings >
4 None.gif
此时客户端代码如下:
 1 ExpandedBlockStart.gif /// <summary>
 2InBlock.gif/// App类
 3ExpandedBlockEnd.gif/// </summary>

 4 None.gif public   class  App
 5 ExpandedBlockStart.gif {
 6InBlock.gif    public static void Main(string[] args)
 7ExpandedSubBlockStart.gif    {
 8InBlock.gif        string strfactoryName = ConfigurationSettings.AppSettings["factoryName"];
 9InBlock.gif        
10InBlock.gif        LogFactory factory;
11InBlock.gif        factory = (LogFactory)Assembly.Load("FactoryMethod").CreateInstance("FactoryMethod." + strfactoryName);
12InBlock.gif
13InBlock.gif        Log log = factory.Create();
14InBlock.gif        log.Write();
15ExpandedSubBlockEnd.gif    }

16ExpandedBlockEnd.gif}

17 None.gif
现在我们看到,在引进新产品(日志记录方式)的情况下,我们并不需要去修改工厂类,而只是增加新的产品类和新的工厂类(注意:这是任何时候都不能避免的),这样很好的符合了开放封闭原则。
ASP.NET HTTP 通道中的应用
Factory Method 模式在 ASP.NET HTTP 通道中我们可以找到很多的例子。 ASP.NET HTTP 通道是 System.Web 命名空间下的一个类, WEB Server 使用该类处理接收到的 HTTP 请求,并给客户端发送响应。 HTTP 通道主要的工作有 Session 管理,应用程序池管理,缓存管理,安全等。
System.Web.HttpApplicationFactory
HttpRuntime HTTP 通道的入口点,它根据每一个具体的请求创建一个 HttpContext 实例,  HttpRuntime 并没有确定它将要处理请求的 HttpApplication 对象的类型,它调用了一个静态的工厂方法 HttpApplicationFactory.GetApplicationInstance ,通过它来创建 HttpContext 实例。 GetApplicationInstance 使用 HttpContext 实例来确定针对这个请求该响应哪个虚拟路径,如果这个虚拟路径以前请求过, HttpApplication (或者一个继承于 ASP.Global_asax 的类的实例)将直接从应用程序池中返回,否则针对该虚拟路径将创建一个新的 HttpApplication 对象并返回。如下图所示:
HttpApplicationFactory.GetApplicationInstance 带有一个类型为 HttpContext 的参数,创建的所有对象(产品)都是 HttpApplication 的类型,通过反编译,来看一下 GetApplicationInstance 的实现:

 1 None.gif internal   static  IHttpHandler GetApplicationInstance(HttpContext context)
 2 ExpandedBlockStart.gif {
 3InBlock.gif      if (HttpApplicationFactory._customApplication != null)
 4ExpandedSubBlockStart.gif      {
 5InBlock.gif            return HttpApplicationFactory._customApplication;
 6ExpandedSubBlockEnd.gif      }

 7InBlock.gif      if (HttpDebugHandler.IsDebuggingRequest(context))
 8ExpandedSubBlockStart.gif      {
 9InBlock.gif            return new HttpDebugHandler();
10ExpandedSubBlockEnd.gif      }

11InBlock.gif      if (!HttpApplicationFactory._theApplicationFactory._inited)
12ExpandedSubBlockStart.gif      {
13InBlock.gif            lock (HttpApplicationFactory._theApplicationFactory)
14ExpandedSubBlockStart.gif            {
15InBlock.gif                  if (!HttpApplicationFactory._theApplicationFactory._inited)
16ExpandedSubBlockStart.gif                  {
17InBlock.gif                        HttpApplicationFactory._theApplicationFactory.Init(context);
18InBlock.gif                        HttpApplicationFactory._theApplicationFactory._inited = true;
19ExpandedSubBlockEnd.gif                  }

20ExpandedSubBlockEnd.gif            }

21ExpandedSubBlockEnd.gif      }

22InBlock.gif      return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);
23ExpandedBlockEnd.gif}

24 None.gif
System.Web.IHttpHandlerFactory
我们来做进一步的探索, HttpApplication 实例需要一个 Handler 对象来处理资源请求,  HttpApplication 的主要任务就是找到真正处理请求的类。 HttpApplication 首先确定了一个创建 Handler 对象的工厂,来看一下在 Machine.config 文件中的配置区 <httphandlers> ,在配置文件注册了应用程序的具体处理类。例如在 Machine.config 中对 *.aspx 的处理将映射到 System.Web.UI.PageHandlerFactory  类,而对 *.ashx 的处理将映射到 System.Web.UI.SimpleHandlerFactory   类,这两个类都是继承于 IhttpHandlerFactory 接口的具体类
None.gif < httpHandlers >
None.gif
None.gif
< add  verb ="*"  path ="*.aspx"  type ="System.Web.UI.PageHandlerFactory"   />
None.gif
None.gif
< add  verb ="*"  path ="*.ashx"  type ="System.Web.UI.SimpleHandlerFactory"   />
None.gif
None.gifdot.gifdot.gif
None.gif
None.gif
</ httpHandlers >
None.gif
这个配置区建立了资源请求的类型和处理请求的类之间的一个映射集。如果一个 .aspx 页面发出了请求,将会调用 System.Web.UI.PageHandlerFactory 类, HttpApplication 调用接口 IHttpHandlerFactory 中的工厂方法 GetHandler 来创建一个 Handler 对象。当一个名为 sample.aspx 的页面发出请求时,通过 PageHandlerFactory 将返回一个 ASP.SamplePage_aspx 对象(具体产品),如下图:
IHttpHandlerFactory 工厂:

1 None.gif public   interface  IHttpHandlerFactory
2 ExpandedBlockStart.gif {
3InBlock.gif      // Methods
4InBlock.gif      IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
5InBlock.gif      void ReleaseHandler(IHttpHandler handler);
6ExpandedBlockEnd.gif}

7 None.gif
IHttpHandlerFactory.GetHandler 是一个工厂方法模式的典型例子,在这个应用中,各个角色的设置如下:
抽象工厂角色: IHttpHandlerFactory
具体工厂角色: PageHandlerFactory
抽象产品角色: IHttpHandler
具体产品角色: ASP.SamplePage_aspx
进一步去理解
理解上面所说的之后,我们就可以去自定义工厂类来对特定的资源类型进行处理。第一步我们需要创建两个类去分别实现 IHttpHandlerFactory   IHttpHandler 这两个接口。
 1 ExpandedBlockStart.gif public   class  HttpHandlerFactoryImpl:IHttpHandlerFactory  {
 2InBlock.gif   
 3InBlock.gif   IHttpHandler IHttpHandlerFactory.GetHandler(
 4InBlock.gif      HttpContext context, String requestType, 
 5ExpandedSubBlockStart.gif      String url, String pathTranslated ) {
 6InBlock.gif
 7InBlock.gif         return new HttpHandlerImpl();
 8InBlock.gif         
 9ExpandedSubBlockEnd.gif   }
//IHttpHandlerFactory.GetHandler
10InBlock.gif
11InBlock.gif   void IHttpHandlerFactory.ReleaseHandler(
12ExpandedSubBlockStart.gif      IHttpHandler handler) /*no-op*/ }
13InBlock.gif
14ExpandedBlockEnd.gif}
// HttpHandlerFactoryImpl
15 None.gif
16 ExpandedBlockStart.gif public   class  HttpHandlerImpl:IHttpHandler  {
17InBlock.gif
18ExpandedSubBlockStart.gif   void IHttpHandler.ProcessRequest(HttpContext context) {
19InBlock.gif      
20InBlock.gif      context.Response.Write("sample handler invokeddot.gif");
21InBlock.gif      
22ExpandedSubBlockEnd.gif   }
//ProcessRequest
23InBlock.gif
24ExpandedSubBlockStart.gif   bool IHttpHandler.IsReusable get return false; } }
25InBlock.gif
26ExpandedBlockEnd.gif}
// HttpHandlerImpl
27 None.gif
第二步需要在配置文件中建立资源请求类型和处理程序之间的映射。我们希望当请求的类型为 *.sample 时进入我们自定义的处理程序,如下:
None.gif < httpHandlers >
None.gif
None.gif   
< add  verb ="*"  path ="*.sample"  
None.gif
None.gif      type
="HttpHandlerFactoryImpl,SampleHandler"   />
None.gif
None.gif
</ httpHandlers >
None.gif
最后一步我们需要把文件扩展 *.sample 映射到 ASP.NET ISAPI 扩展 DLL aspnet_isapi.dll )上。由于我们已经建立了用于处理新扩展文件的处理程序了,我们还需要把这个扩展名告诉 IIS 并把它映射到 ASP.NET 。如果你不执行这个步骤而试图访问 *.sample 文件, IIS 将简单地返回该文件而不是把它传递给 ASP.NET 运行时。其结果是该 HTTP 处理程序不会被调用。
运行 Internet 服务管理器,右键点击默认 Web 站点,选择属性,移动到主目录选项页,并点击配置按钮。应用程序配置对话框弹出来了。点击添加按钮并在可执行字段输入 aspnet_isapi.dll 文件路径,在扩展字段输入 .sample 。其它字段不用处理;该对话框如下所示:
.NET Framework 中,关于工厂模式的使用有很多的例子,例如 IEnumerable IEnumerator 就是一个 Creator 和一个 Product System.Security.Cryptography 中关于加密算法的选择, SymmetricAlgorithm AsymmetricAlgorithm HashAlgorithm 分别是三个工厂,他们各有一个静态的工厂方法 Create System.Net.WebRequest  .NET Framework  的用于访问  Internet  数据的请求 / 响应模型的抽象基类。使用该请求 / 响应模型的应用程序可以用协议不可知的方式从  Internet  请求数据。在这种方式下,应用程序处理  WebRequest  类的实例,而协议特定的子类则执行请求的具体细节。请求从应用程序发送到某个特定的  URI ,如服务器上的  Web  页。 URI  从一个为应用程序注册的  WebRequest  子代列表中确定要创建的适当子类。注册  WebRequest  子代通常是为了处理某个特定的协议(如  HTTP   FTP ),但是也可以注册它以处理对特定服务器或服务器上的路径的请求 。有时间我会就 .NET Framework 中工厂模式的使用作一个专题总结。
实现要点
1.   Factory Method 模式的两种情况:一是Creator类是一个抽象类且它不提供它所声明的工厂方法的实现;二是Creator是一个具体的类且它提供一个工厂方法的缺省实现。
2.   工厂方法是可以带参数的。
3.   工厂的作用并不仅仅只是创建一个对象,它还可以做对象的初始化,参数的设置等。
效果
1.   用工厂方法在一个类的内部创建对象通常比直接创建对象更灵活。
2.   Factory Method 模式通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。
适用性
在以下情况下,适用于工厂方法模式:
1.        当一个类不知道它所必须创建的对象的类的时候。
2.        当一个类希望由它的子类来指定它所创建的对象的时候。
3.        当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
总结
Factory Method 模式是设计模式中应用最为广泛的模式,通过本文,相信读者已经对它有了一定的认识。然而我们要明确的是:在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要。Factory Method要解决的就是对象的创建时机问题,它提供了一种扩展的策略,很好地符合了开放封闭原则。 __________________________________________________________________________________
参考文献:
《设计模式》(中文版)
MSDN :《Exploring the Factory Design Pattern
DesignPatternsExplained











本文转自lihuijun51CTO博客,原文链接: http://blog.51cto.com/terrylee/67748  ,如需转载请自行联系原作者

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
SQL 开发框架 数据可视化
企业应用开发中.NET EF常用哪种模式?
企业应用开发中.NET EF常用哪种模式?
|
4月前
|
SQL 开发框架 数据可视化
企业应用开发中.NET EF常用哪种模式?
企业应用开发中.NET EF常用哪种模式?
|
6月前
|
设计模式 算法
设计模式9 - 模板方法模式【Template Method Pattern】
设计模式9 - 模板方法模式【Template Method Pattern】
21 0
|
5月前
|
设计模式 Java 程序员
认真学习设计模式之工厂模式(Factory Pattern)
认真学习设计模式之工厂模式(Factory Pattern)
34 0
|
6月前
|
设计模式 Java
设计模式6 - 抽象工厂模式【Abstract Factory Pattern】
设计模式6 - 抽象工厂模式【Abstract Factory Pattern】
20 0
|
6月前
|
设计模式
设计模式5 - 工厂方法模式【Factory Method Pattern】
设计模式5 - 工厂方法模式【Factory Method Pattern】
20 0
|
6月前
|
设计模式 算法 Java
【设计模式——学习笔记】23种设计模式——模板方法模式Template Method(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——模板方法模式Template Method(原理讲解+应用场景介绍+案例介绍+Java代码实现)
22 0
|
6月前
|
设计模式 Java 程序员
【设计模式——学习笔记(下)】23种设计模式——工厂模式Factory(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——工厂模式Factory(原理讲解+应用场景介绍+案例介绍+Java代码实现)
37 0
|
6月前
|
设计模式 Java 关系型数据库
【设计模式——学习笔记(上)】23种设计模式——工厂模式Factory(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——工厂模式Factory(原理讲解+应用场景介绍+案例介绍+Java代码实现)
39 0
|
10天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
19 0