创建型模式
目录
1.4 另一个例子,实现加减乘除,简单工厂模式与工厂模式异同比较
1、工厂模式
工厂模式方法(factory method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类。
1.1 工厂模式的UML类图
1.2 日常生活看工厂模式
《速度与激情》范·迪塞尔要去参加五环首届跑车拉力赛的场景。因为要拍摄《速度与激情8》,导演组车的种类增多了,阵容也更加豪华了,加上导演古怪的性格可能每一场戏绝对需要试驾几十种车。如果车库没有的车(具体产品类)可以由场务(具体工厂类)直接去4S店取,这样没增加一种车(具体产品类)就要对应的有一个场务(具体工厂类),他们互相之间有着各自的职责,互不影响,这样可扩展性就变强了。
1.3 具体例子——拍摄《速度与激情8》需要车库外的车为例
抽象工厂代码:
namespace CNBlogs.DesignPattern.Common
{
public interface IFactory
{
ICar CreateCar();
}
}
抽象产品代码:
namespace CNBlogs.DesignPattern.Common
{
public interface ICar
{
void GetCar();
}
}
具体工厂代码:
namespace CNBlogs.DesignPattern.Common
{
/// <summary>
/// 具体工厂类: 用于创建跑车类
/// </summary>
public class SportFactory : IFactory
{
public ICar CreateCar()
{
return new SportCar();
}
}
/// <summary>
/// 具体工厂类: 用于创建越野车类
/// </summary>
public class JeepFactory : IFactory
{
public ICar CreateCar()
{
return new JeepCar();
}
}
/// <summary>
/// 具体工厂类: 用于创建两厢车类
/// </summary>
public class HatchbackFactory : IFactory
{
public ICar CreateCar()
{
return new HatchbackCar();
}
}
}
具体产品代码:
namespace CNBlogs.DesignPattern.Common
{
/// <summary>
/// 具体产品类: 跑车
/// </summary>
public class SportCar : ICar
{
public void GetCar()
{
Console.WriteLine("场务把跑车交给范·迪塞尔");
}
}
/// <summary>
/// 具体产品类: 越野车
/// </summary>
public class JeepCar : ICar
{
public void GetCar()
{
Console.WriteLine("场务把越野车交给范·迪塞尔");
}
}
/// <summary>
/// 具体产品类: 两箱车
/// </summary>
public class HatchbackCar : ICar
{
public void GetCar()
{
Console.WriteLine("场务把两箱车交给范·迪塞尔");
}
}
}
客户端代码:
namespace CNBlogs.DesignPattern
{
using System.IO;
using System.Configuration;
using System.Reflection;
using CNBlogs.DesignPattern.Common;
class Program
{
static void Main(string[] args)
{
// 工厂类的类名写在配置文件中可以方便以后修改
string factoryType = ConfigurationManager.AppSettings["FactoryType"];
// 这里把DLL配置在数据库是因为以后数据可能发生改变
// 比如说现在的数据是从sql server取的,以后需要从oracle取的话只需要添加一个访问oracle数据库的工程就行了
string dllName = ConfigurationManager.AppSettings["DllName"];
// 利用.NET提供的反射可以根据类名来创建它的实例,非常方便
var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
string codeBase = currentAssembly.CodeBase.ToLower().Replace(currentAssembly.ManifestModule.Name.ToLower(), string.Empty);
IFactory factory = Assembly.LoadFrom(Path.Combine(codeBase, dllName)).CreateInstance(factoryType) as IFactory;
ICar car = factory.CreateCar();
car.GetCar();
}
}
}
1.4 另一个例子,实现加减乘除,简单工厂模式与工厂模式异同比较
1.4.1 简单工厂模式实现加减乘除
在这个例子中,只需要输入运算符,工厂就实例化出合适的对象,通过多态返回父类的方式实现了计算器的结果。而客户端只要这样调用就可以了,如下:
1.4.2 工厂模式实现加减乘除
就要先创建一个工厂接口,然后加减乘除各建一个工厂方法去实现这个接口。例子如下:
参考例子:https://blog.csdn.net/abc709272013/article/details/52654266
2、工厂模式在Logback源码中的应用
我们再看logback中的一些应用:
public interface ILoggerFactory {
/**
* Return an appropriate {@link Logger} instance as specified by the
* <code>name</code> parameter.
*
* <p>If the name parameter is equal to {@link Logger#ROOT_LOGGER_NAME}, that is
* the string value "ROOT" (case insensitive), then the root logger of the
* underlying logging system is returned.
*
* <p>Null-valued name arguments are considered invalid.
*
* <p>Certain extremely simple logging systems, e.g. NOP, may always
* return the same logger instance regardless of the requested name.
*
* @param name the name of the Logger to return
* @return a Logger instance
*/
public Logger getLogger(String name);
}
这个接口下边只有一个方法,我们看一下他的实现,在这三个类中
也就是把这个抽象方法教给子类来实现,我们来看一下类图
Logback在创建Log时,使用的就是工厂方法。它的各个元素都是在同一个类sun.rmi.runtime的Log.java中用内部类实现,但这并不影响工厂方法的结构。它的抽象产品基类就是Log,其中一个产品实现类为LoggerLog,抽象工厂类为LogFactory,工厂实现类为LoggerLogFactory。另一对是LogStreamLog和LogStreamLogFactory。其中关键代码如下,为了更加易懂,我调整了一下源码顺序:
public abstract class Log {
... ...
public abstract void log(Level var1, String var2);
private interface LogFactory {
Log createLog(String var1, String var2, Level var3);
}
private static class LoggerLog extends Log {
public void log(Level var1, String var2) {
if (this.isLoggable(var1)) {
String[] var3 = Log.getSource();
this.logger.logp(var1, var3[0], var3[1], Thread.currentThread().getName() + ": " + var2);
}
}
}
private static class LoggerLogFactory implements Log.LogFactory {
public Log createLog(String var1, String var2, Level var3) {
Logger var4 = Logger.getLogger(var1);
return new Log.LoggerLog(var4, var3);
}
}
private static class LogStreamLog extends Log {
public void log(Level var1, String var2) {
if (this.isLoggable(var1)) {
String[] var3 = Log.getSource();
this.stream.println(unqualifiedName(var3[0]) + "." + var3[1] + ": " + var2);
}
}
}
private static class LogStreamLogFactory implements Log.LogFactory {
public Log createLog(String var1, String var2, Level var3) {
LogStream var4 = null;
if (var2 != null) {
var4 = LogStream.log(var2);
}
return new Log.LogStreamLog(var4, var3);
}
}
}
3、工厂模式在Dubbo源码中的应用
ServiceConfig中有个字段,代码是这样的:
查看文本打印
private static final Protocol protocol =ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Dubbo里有很多这种代码。这也是一种工厂模式,只是实现类的获取采用了jdkspi的机制。这么实现的优点是可扩展性强,想要扩展实现,只需要在classpath下增加个文件就可以了,代码零侵入。另外,像上面的Adaptive实现,可以做到调用时动态决定调用哪个实现,但是由于这种实现采用了动态代理,会造成代码调试比较麻烦,需要分析出实际调用的实现类。
4、简单工厂和工厂的区别
4.1 简单工厂模式优点
工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
就像这个计算器,让客户端不用管该用哪个类的实例,只需要把“+”给工厂,工厂自动就给出了相应的实例,客户端只要去运算就可以了,不同的实例会实现不同的运算。
4.2 工厂模式
工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。你想要加功能,本来是要改工厂类的,而现在是修改客户端。
4.3 工厂模式优点
工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。
这两者都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户程序与产品对象的耦合。
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
4.4 工厂模式缺点
缺点是由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。
参考文章:
https://www.cnblogs.com/toutou/p/4899388.html
https://blog.csdn.net/abc709272013/article/details/52654266
https://blog.csdn.net/weixin_41657493/article/details/88361421
https://blog.csdn.net/zuoanyinxiang/article/details/51330415