设计模式学习笔记六:.NET反射工厂

  1. 简述

    通过前面的学习,我们以传统的方式实现了简单工厂,工厂方法和抽象工厂,但是有些场合下如此处理,代码会变得冗余并且难以维护。假设我们要创建交通工具。可以是汽车,火车,轮船等,其结构如下:


     我们可以采用简单工厂,通过参数指示创建所需要的对象类型。如果要增加子类,例如卡车和轿车,则必须增加参数和相应的代码。如果子类层次过多,则会是程序变得很难维护。 
    但我们可以采用工厂方法模式来实现,即定义一个产生交通工具的接口,然后在子类中实现创建具体子类。代码如下:
Code
public interface ICreateVehicle
    
{
         Vehicle CreateCehicle();
    }
Code
 public abstract class Vehicle
    
{

    }
Code
    public class Car:Vehicle
    
{
        
public Car()
        
{
            Console.WriteLine(
"创建了一个Car");
        }

    }
Code
    public class Boat:Vehicle 
    
{
        
public Boat()
        
{
            Console.WriteLine(
"创建了一个Boat");
        }

    }
Code
    public class CreateCar:ICreateVehicle
    
{
        
ICreateVehicle 成员#region ICreateVehicle 成员

        
public  Vehicle CreateCehicle()
        
{
            Vehicle vehicle
=new Car();
            
return vehicle;
        }


        
#endregion

    }
Code
    public class CreateBoat:ICreateVehicle
    
{
        
ICreateVehicle 成员#region ICreateVehicle 成员

        
public Vehicle CreateCehicle()
        
{
            Vehicle vehicle 
= new Boat();
            Console.WriteLine(
"Car");
            
return vehicle;
        }


        
#endregion

    }

这就是工厂方法。如果希望增加新的交通工具,不仅需要实现交通工具接口,还需要实现生产交通工具的工厂方法。

    显然我们需要几十种交通工具,则需要几十个具体的工厂。而这些类的区别仅仅是返回相对应的类的实例,所以位维护带来了很大的麻烦。如果需要在接口中增加一个带参数的方法,则所有的子类都需要修改。
在这种场合下,采用抽象工厂与工厂方法没有区别,因为这里并不涉及产品线,抽象工厂并不能解决其中的问题,如果每种交通工具都要有对应的车站,则要使用抽象工厂,但是将会跟复杂。

有没有可能将需要创建类的类型传递到工厂方法中,由工厂方法根据类型返回相应的实例?解决这个问题的关键是需要动态的决定需要创建的类,这不是设计模式能解决的问题,属于软件平台的功能范畴。.NET可以提供反射技术。

我们先看通过反射技术实现的简化的工厂,代码如下:

Code
 public class CreateVehicleByType:ICreateVehicle
    
{

        
ICreateVehicle 成员#region ICreateVehicle 成员

        
private Type VehicleTYpe;

        
public CreateVehicleByType(string strType)
        
{
            Type t 
= Type.GetType(strType);

            VehicleTYpe 
= t;
        }

        
public  Vehicle CreateCehicle()
        
{
            ConstructorInfo  objConstrutor 
= VehicleTYpe.GetConstructor(System.Type.EmptyTypes);
            Vehicle c 
= (Vehicle)objConstrutor.Invoke(null);
            
return c;
        }


        
#endregion


       
    }

在使用是,只要在创建时带入需要创建的类的类型:

Code
static void Main(string[] args)
        
{

            
string strType = "Car";

            Vehicle v;
            ICreateVehicle f 
= null;

            
if (strType == "Car")
            
{
                f 
= new CreateVehicleByType("FactoryVehicle.Car");

            }

            
else if (strType == "Boat")
            
{
                f 
= new CreateVehicleByType("Boat");
            }

            v 
= f.CreateCehicle();
            Console.ReadLine();

        }


通过反射技术,我们将很多的具体的工厂类简化为一个类,并且新增加类型时不需要新的工厂类,这样我们得到简化的工厂,可以称其为“反射工厂”。

2.实例

         先来看看,大话设计模式中的利用反射加抽象工厂的数据访问程序。先来看看反射技术的基本格式:

Assembly.Load(“程序集名称”).CreateInstance(“命名空间.类名称”);

只要在程序顶端写上using System.Reflection来引用Reflection,就可以采用反射工厂来克服抽象工厂模式的先天不足。下面我们来看通过反射技术实现不同数据库的访问程序.

         先来看结构图:



 

DataAccess类,用反射技术,取代了抽象工厂中的IFactory,SqlServerFactoryAccessFactory
    
具体代码:
    

Code
public class User
    
{
        
private int _id;
        
public int ID
        
{
            
get return _id; }
            
set { _id = value; }
        }


        
private string _name;
        
public string Name
        
{
            
get return _name; }
            
set { _name = value; }
        }

    }


    
public class Department
    
{
        
private int _id;
        
public int ID
        
{
            
get return _id; }
            
set { _id = value; }
        }


        
private string _deptName;
        
public string DeptName
        
{
            
get return _deptName; }
            
set { _deptName = value; }
        }

    }


    
public interface IUser
    
{
        
void Insert(User user);

        User GetUser(
int id);
    }


    
public class SqlserverUser : IUser
    
{
        
public void Insert(User user)
        
{
            Console.WriteLine(
"在Sqlserver中给User表增加一条记录");
        }


        
public User GetUser(int id)
        
{
            Console.WriteLine(
"在Sqlserver中根据ID得到User表一条记录");
            
return null;
        }

    }


    
public class AccessUser : IUser
    
{
        
public void Insert(User user)
        
{
            Console.WriteLine(
"在Access中给User表增加一条记录");
        }


        
public User GetUser(int id)
        
{
            Console.WriteLine(
"在Access中根据ID得到User表一条记录");
            
return null;
        }

    }


    
public interface IDepartment
    
{
        
void Insert(Department department);

        Department GetDepartment(
int id);
    }


    
public class SqlserverDepartment : IDepartment
    
{
        
public void Insert(Department department)
        
{
            Console.WriteLine(
"在Sqlserver中给Department表增加一条记录");
        }


        
public Department GetDepartment(int id)
        
{
            Console.WriteLine(
"在Sqlserver中根据ID得到Department表一条记录");
            
return null;
        }

    }


    
public class AccessDepartment : IDepartment
    
{
        
public void Insert(Department department)
        
{
            Console.WriteLine(
"在Access中给Department表增加一条记录");
        }


        
public Department GetDepartment(int id)
        
{
            Console.WriteLine(
"在Access中根据ID得到Department表一条记录");
            
return null;
        }

    }


    
public class DataAccess
    
{
        
private static readonly string AssemblyName = "抽象工厂模式";
        
private static readonly string db = "Sqlserver";
        
//private static readonly string db = "Access";

        
public static IUser CreateUser()
        
{
            
string className = AssemblyName + "." + db + "User";
            
return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
        }


        
public static IDepartment CreateDepartment()
        
{
            
string className = AssemblyName + "." + db + "Department";
            
return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
        }

    }

调用代码:

Code
            User user = new User();
            Department dept 
= new Department();

            IUser iu 
= DataAccess.CreateUser();

            iu.Insert(user);
            iu.GetUser(
1);

            IDepartment id 
= DataAccess.CreateDepartment();
            id.Insert(dept);
            id.GetDepartment(
1);

            Console.Read();
        }


    现在我们要增加Oracle数据访问,相关类的增加是不可避免的,这点是无论我们用什么方法都解决不了的,这是扩展,依照开发-封闭原则,对于扩展,我们开放,但对与修改我们关闭。就现在的代码中,我们要换Oracle很容易,只需将db=”Sqlserver”换成db=”Oracle”
    
现在我们需要增加Product,只需增加三个与Product相关的类,再修改一下DataAccess,在其中增加一个创建Product的方法就可以了。
    
现在我们要更换数据访问程序是,我们还需要修改程序,重新编译,我们可以利用配置文件来解决这个问题,首先要在我们的项目中添加config文件,内容如下:

Code
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
<appSettings>
        
<add key="DB" value="Sqlserver"/>
    
</appSettings>
</configuration>


再在项目中引用

System.configuration,并在程序头增加using System.configuration;, 然后修改DataAccess类的字段db的赋值代码:
Code
class DataAccess
    
{
        
private static readonly string AssemblyName = "抽象工厂模式";
        
private static readonly string db = ConfigurationManager.AppSettings["DB"];
        
        
public static IUser CreateUser()
        
{
            
string className = AssemblyName + "." + db + "User";
            
return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
        }


        
public static IDepartment CreateDepartment()
        
{
            
string className = AssemblyName + "." + db + "Department";
            
return (IDepartment)Assembly.Load(AssemblyName).CreateInstance(className);
        }

    }


3.总结

         使用反射工厂的优点是极大的减少了工厂类的数量,降低了代码的冗余,并且系统更容易扩展,增加新类型后,不需要修改工厂类。

         使用反射工厂的代价是工厂与产品之间的依赖关系不明显,由于动态绑定,因此理论上可以用一个工厂完成很多类型的实例化,从而使得代码不容易理解。另外就是增加了测试难度,因为创建是动态完成的。

         采用反射技术创建的反射工厂可以使系统更灵活,使工厂和产品之间的依赖关系更小。在.NET的项目中大量的使用了反射工厂取代的传统的工厂。

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

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

相关文章

在unity 中,使用http请求,下载文件到可读可写路径

在这里我用了一个线程池&#xff0c;线程池参数接收一个带有object参数的&#xff0c;无返回值的委托 &#xff0c;下载用到的核心代码&#xff0c;网上拷贝的&#xff0c;他的核心就是发起一个web请求&#xff0c;然后得到请求的响应&#xff0c;读取响应的流 剩下的都是常见的…

在tinyalsa上抓取音频

我们经常会遇到这样的问题&#xff0c;应用读取到的音频有问题&#xff0c;需要在tinyalsa里面读取音频来确认是底层音频有问题&#xff0c;还是应用处理之后存在的问题。所以&#xff0c;这个patch就出现了代码的逻辑很简单&#xff0c;主要是在pcm_read的时候&#xff0c;同时…

STM32——GPIO(1)

STM32——GPIO 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 【对单片机的操作就是控制IO口】 一、GPIO&#xff08;通用输入输出口&#xff09; 1、选定需要的引脚&#xff08;对应哪一个IO口&#xff09;&#xff1b; 2、配置需要的功能&#xf…

【opencv学习笔记八】创建TrackBar轨迹条

createTrackbar这个函数我们以后会经常用到&#xff0c;它创建一个可以调整数值的轨迹条&#xff0c;并将轨迹条附加到指定的窗口上&#xff0c;使用起来很方便。首先大家要记住&#xff0c;它往往会和一个回调函数配合起来使用。先看下他的函数原型&#xff1a; int createTra…

父母悄悄给自己买房,我很生气,要怎么调整心态?

——问题我是独生子&#xff0c;今年满24岁刚上研一&#xff08;普通211&#xff09;。家庭四川小城市&#xff0c;情况一般&#xff0c;父母二人体制内月薪总计一万元以内&#xff0c;家里积蓄20W-30W&#xff0c;公积金情况不清楚。从小母子关系比较僵硬&#xff0c;母亲小学…

语音处理入门——语音的声学处理

语音的声学处理通常称为特征提取或者信号分析&#xff0c;特征是表示语音信号的一个时间片的矢量。常见的特征类型有LPC&#xff08;线性预测编码&#xff09;特征和PLP&#xff08;感知线性预测编码&#xff09;&#xff0c;该特征称为声谱特征&#xff0c;使用形成波形的不同…

基础呀基础,用二极管防止反接,你学会了吗?

使用新的电源&#xff0c;第一次给设备供电时&#xff0c;要特别注意电源的正负极性标注。比如电源适配器&#xff0c;铭牌上面有标注插头的极性。这个符号说明插头的里面是正极&#xff0c;外面是负极&#xff0c;即“内正外负”。但是也有反过来的&#xff0c;下面这款是“内…

李宏毅的可解释模型——三个任务

1、问题 观看了李宏毅老师的机器学习进化课程之可解释的机器学习&#xff0c;课程中对主要是针对黑盒模型进行白盒模型转化的技巧和方法进行了简单介绍&#xff0c;详细细节可以参考《Interpretable Machine Learning》。像一些线性模型、树形模型是可解释的ML model&#xff…

柔性数组和环形队列之间的故事

之前的文章&#xff0c;讲解了柔性数组&#xff0c;有很多人留言&#xff0c;提到一些问题。刚好&#xff0c;之前发关于环形队列的文章有些问题&#xff0c;这次刚好拿出来一起说一下&#xff0c;并用柔性数组实现一个环形队列。柔性数组的上一篇文章环形队列C语言实现文章1、…

STM32——时钟系统

STM32——时钟系统 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 一、时钟树 普通的MCU&#xff0c;一般只要配置好GPIO 的寄存器&#xff0c;就可以使用了。STM32为了实现低功耗&#xff0c;设计了非常复杂的时钟系统&#xff0c;必须开启外设时钟才…

目标检测发展路程(一)——Two stage

目标检测是计算机视觉领域中非常重要的一个研究方向&#xff0c;它是将图像或者视频中目标与其他不感兴趣的部分进行区分&#xff0c;判断是否存在目标&#xff0c;确定目标位置&#xff0c;识别目标种类的任务&#xff0c;即定位分类。传统的目标检测模型有VJ.Det[1,2],HOG.De…

都2021年了,c/c++开发竟然还能继续吃香??

年后就迎来了金三银四&#xff0c;你准备好2021年的跳槽涨薪计划了吗&#xff1f;今天我就来给大家分享&#xff0c;c/c作为老牌开发常青树&#xff0c;还能与java/python/go较较劲的岗位和技术在哪里&#xff01;同时&#xff0c;给大家整理了2021年系统全面技术学习资料。文末…

目标检测模型——One stage(YOLO v5的模型解析及应用)

1. 简介 目标检测分为Two stage和One stage,只使用一个网络同时产生候选区域并预测出物体的类别和位置&#xff0c;所以它们通常被叫做单阶段检测算法&#xff08;One stage&#xff09;。本篇文章只讲One stage模型&#xff0c;常见的模型有YOLO&#xff0c;SSD。 目标检测发…

腾讯回应QQ读取用户浏览器历史记录

腾讯QQ官方认证账号在知乎回应“QQ扫描读取所有浏览器的历史记录”表示&#xff0c;PC QQ存在读取浏览器历史用以判断用户登录安全风险的情况&#xff0c;读取的数据用于在PC QQ的本地客户端中判断是否恶意登录。所有相关数据不会上传至云端&#xff0c;不会储存&#xff0c;也…

OCR系列——总体概述

最近参加了百度Paddle的动手学OCR课程&#xff0c;特此做一个学习总结。 1. 简介 OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;是计算机视觉重要方向&#xff0c;传统的OCR一般面向扫描文档类对象&#xff0c;现在的OCR是指场景文字识…

STM32——系统滴答定时器

STM32——系统滴答定时器 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 一、SysTick【内核中】 【风格&#xff1a;先描述一下库对寄存器的封装&#xff0c;再举例实现某些功能】 SysTick定时器被捆绑在NVIC中&#xff0c;用于产生SysTick异常&#…

你会用while(1)还是for(;;)写循环代码?

看代码看到for(;;)&#xff0c;然后觉得为什么不写成while(1)呢&#xff0c;所以就做了下面的测试。网上有解释&#xff0c;因为while需要做一次判断&#xff0c;理论上执行会花费的时间更久&#xff0c;for(;;)只是执行了两次空语句&#xff0c;执行会更快for.c#include <s…

OCR系列——文本检测任务

1. 简介 文本检测任务是找出图像或视频中的文字位置。不同于目标检测任务&#xff0c;目标检测不仅要解决定位问题&#xff0c;还要解决目标分类问题。 目标检测和文本检测同属于“定位”问题。但是文本检测无需对目标分类&#xff0c;并且文本形状复杂多样。 当前所说的文本…

关于ORACLE 语句中,IN 超过1000个的解决方法

在ORACLE SELECT 语句中 IN 的数据如果超过 1000&#xff0c;就会出错&#xff0c;解决方法也很简单&#xff0c;以C#代码为例&#xff1a; 1、先写一个方法&#xff0c;接收2个参数 参数1&#xff1a;接收 IN里面的数据&#xff0c;如&#xff1a;a1,a2,...a2000 &#xff1b;…

9个提高代码运行效率的小技巧你知道几个?

我们写程序的目的就是使它在任何情况下都可以稳定工作。一个运行的很快但是结果错误的程序并没有任何用处。在程序开发和优化的过程中&#xff0c;我们必须考虑代码使用的方式&#xff0c;以及影响它的关键因素。通常&#xff0c;我们必须在程序的简洁性与它的运行速度之间做出…