设计模式之C#实现--FactoryMethod
作者:cuike519的专栏 http://blog.csdn.net/cuike519/
工厂方法的目的很明确就是定义一个用来创建对象的接口,但是他不直接创建对象,而由他的子类来创建,这样一来就将创建对象的责任推迟到了该接口的子类中,创建什么类型的对象由子类来决定,而创建对象的时间由接口来定。因此该模式可以在如下几种情况下使用:1、a class can’t predict the class of objects it must create.2、a class wants its subclasses to specify the objects it creates.3、classes delegate responsibility to one of several helper subclasses,but do not know whick helper subclass is the delegate.在这里我们举一个面包房的例子:面包房的面包机好比一个抽象产品,它是一般的面包,那么我们可以通过特殊的加工工艺使它变成不同种类的面包比如:可以是中国式的面包或者是美国的面包等等。这些特殊种类的面包就是我们的具体产品。而做面包的人(也许是自己)就是Creator(创建者),我们使用我们的方法(工厂方法)来做我们想要的面包。这里的灵活性是不言而喻的,使用面包机可以让我们有更多的选择,只有我们在需要的时候才决定吃那一种面包,这不是很好的一件事情吗,如果没有面包机可能我们只能吃中国面包了。下面我们在举几个我们非常熟悉的FCL(.NET Framrwork Class Labrarly)中的例子,IEnumerable和Ienumerator就是一个Creator和一个Product。另一个例子是:WebRequest 是 .NET Framework 的用于访问 Internet 数据的请求/响应模型的抽象(在 Visual Basic 中为 MustInherit)基类。使用该请求/响应模型的应用程序可以用协议不可知的方式从 Internet 请求数据。在这种方式下,应用程序处理 WebRequest 类的实例,而协议特定的子类则执行请求的具体细节。请求从应用程序发送到某个特定的 URI,如服务器上的 Web 页。URI 从一个为应用程序注册的 WebRequest 子代列表中确定要创建的适当子类。注册 WebRequest 子代通常是为了处理某个特定的协议(如 HTTP 或 FTP),但是也可以注册它以处理对特定服务器或服务器上的路径的请求。(WebRequest摘自MSDN),这是一种带参数的工厂方法。在FCL里面工厂方法是一种最常见的模式应用。
以上据了一个简单的例子(面包),不知道是不是贴切,我不想重新描写一次该模式,但是再有必要的时候我会加以强调的,下面就用创建型模式的结构图来展现这个模式。
为了实现工厂模式来创建迷宫我们有一下的类图,从图中可以看出Creator(MazeGame)里面有很多的工厂方法,用户可以通过它来得到不同的房子(组成迷宫的组件),但是也许我们不知道我们的MazeGame是要创建什么样的Room所以我们可以定义一些子类(现在有两个)用来实现不同的创建工作,Room是组成我们的迷宫的最常见的房子,MazeGame可以产生这样大的房子但是为了满足不同的要求我们可以定义一些特殊的房子比如:带炸弹的房子(是指墙上有炸弹)和可以施魔法的房子(可以对门施魔法)。这样我们可以不改变原来的接口,用MazeGame的子类来完成这些特殊的创建工作,就像下面的图中所表示的一样。
我们把创建的细节推迟到了子类去实现,这样很大程度上提高了程序的韧性,如果我们想创建一个新的房子或者新的门我们可以在不改变原来代码的情况下添加以个MazeGame子类来实现这个想法。但是我觉得这有一个问题为了创建一个新的房子我们必须创建一个新的子类来满足这个要求,这样看起来好像是一对一对的平行的样子。这样会使我们的Creator的子类越来越多,我们可以使用带参数的工厂方法减少这种类的繁殖,下面实现的代码并没有实现这种带参数的工厂方法,网友们可以自己实现。我们只要给MakeWall和MakeDoor传递一个参数来确定创建什么样的Wall和Door,不过我们需要在默认的MazeGame里相应的方法写重裁版本。
好了废话就不多说了,GOF说的比我清楚多了(其实我也是从他们那里学的J)。下面来实现我们的迷宫,代码如下:
首先实现我们的环境
using System;
using System.Collections;
// 该命名空间中是一些运行实例德的环境包括Maze、Door等等
namespace CommonObject{
// 所有的迷宫构件的基类里面有一个方法用来显示当前构件的信息
public class MapSite{
public virtual string Enter(){
return string.Empty;
}
}
// 墙是组成迷宫的构件之一,这里是一个很一般的墙(没有炸弹)
public class Wall : MapSite{
public override string Enter(){
return "This is a Wall.";
}
public Wall(){}
}
// 门也是迷宫的组成部分之一,这也是一个很普通的门(不能施魔法)
public class Door : MapSite{
public override string Enter(){
return "This is a Door.";
}
// 门是在两个房子之间的构件所以构造函数包含两个房子
// 说明是哪两个房子之间的门
public Door(Room roomFrom,Room roomTo){
this.m_Room1 = roomFrom;
this.m_Room2 = roomTo;
}
// 让我们有机会可以从门进入另一个房子,将会得到
// 另一个房子的引用
public Room OtherSideFrom(Room roomFrom){
if(this.m_Room1 == roomFrom)
return this.m_Room2;
else
return this.m_Room1;
}
// 这是一些私有的变量
private Room m_Room1;
private Room m_Room2;
// 描述门的状态默认所有的新门都是关的
private bool m_IsOpen = false;
// 提供一个公共访问的访问器
public bool IsOpen{
set{this.m_IsOpen = value;}
get{return this.m_IsOpen;}
}
}
// 房间是组成迷宫的基本单位
public class Room : MapSite
{
// 枚举类型表示房子的面
public enum Sides{
North = 0,
East,
South,
West
}
public override string Enter(){
return "This is a Room.";
}
// 构造函数,为了可以区分房间我们给每一个房间加一个标示
public Room(int roomNumber){
this.m_roomNumber = roomNumber;
}
// 设置房子的面,房子有4个面组成,因为我们现在不知道每个面
// 的具体类型(门?墙?)所以我们用MapSite类型。
public void SetSide(Sides side,MapSite sideMap){
this.m_side[(int)side] = sideMap;
}
// 得到指定的面,同样我们不知道得到的是哪一个面
// 所以我们用MapSite返回结构
public MapSite GetSide(Sides side){
return this.m_side[(int)side];
}
// 一些私有成员 房号
protected int m_roomNumber;
// 房子有4个面
protected const int m_Sides = 4;
// 用一个1维的MapSite数组存储未知类型的面(墙或门)
protected MapSite[] m_side = new MapSite[m_Sides];
}
// 带炸弹的房子
public class BombedRoom : Room{
public BombedRoom(int roomNumber) : base(roomNumber){}
}
// 带迷宫的房子
public class EnchantedRoom : Room{
public EnchantedRoom(int roomNumber,Spell spell) : base(roomNumber){}
}
// 这是一个特殊的墙--带炸弹的
public class BombedWall : Wall{}
// 这是一个可以施魔法的门
public class EnchantedDoor : Door{
public EnchantedDoor(Room roomFrom,Room roomTo) : base(roomFrom,roomTo){}
}
// 这就是我们的迷宫了
public class Maze{
private ArrayList m_Rooms = new ArrayList();
public Room RoomNumber(int roomNumber){
return (Room)this.m_Rooms[roomNumber];
}
public void AddRoom(Room room){
this.m_Rooms.Add(room);
}
}
// 魔法类
public class Spell{}
}
接下来是工厂方法的实现:
using System;
using System.Collections;
namespace FactoryMethod_Example
{
// 加入MazeContext
using CommonObject;
// 实现了一些工厂方法的Creator类
public class MazeGame
{
// 要返回给Client的对象
private Maze m_Maze = null;
// 一个访问器用来得到maze
public Maze Maze
{
get
{
if (this.m_Maze == null)
this.m_Maze = CreateMaze();
return this.m_Maze;
}
}
// 构造器
public MazeGame()
{
}
// 以下就是一些工厂方法创建迷宫的每个个构件
public virtual Maze MakeMaze()
{
return new Maze();
}
public virtual Room MakeRoom(int id)
{
return new Room(id);
}
public virtual Wall MakeWall()
{
return new Wall();
}
public virtual Door MakeDoor(Room room1, Room room2)
{
return new Door(room1, room2);
}
// 创建迷宫
public Maze CreateMaze()
{
Maze maze = MakeMaze();
// 创建门和房间
Room room1 = MakeRoom(1);
Room room2 = MakeRoom(2);
Door theDoor = MakeDoor(room1, room2);
// 将房间添加到迷宫里面
maze.AddRoom(room1);
maze.AddRoom(room2);
// 设置room1的面
room1.SetSide(Room.Sides.North, MakeWall());
room1.SetSide(Room.Sides.East, theDoor);
room1.SetSide(Room.Sides.South, MakeWall());
room1.SetSide(Room.Sides.West, MakeWall());
// 设置room2的面
room2.SetSide(Room.Sides.North, MakeWall());
room2.SetSide(Room.Sides.East, MakeWall());
room2.SetSide(Room.Sides.South, MakeWall());
room2.SetSide(Room.Sides.West, theDoor);
return maze;
}
}
// 创建带炸弹迷宫
public class BombedMazeGame : MazeGame{
public BombedMazeGame(){}
public override Wall MakeWall(){
return new BombedWall();
}
public override Room MakeRoom(int n){
return new BombedRoom(n);
}
}
// 创建带魔法的迷宫
public class EnchantedMazeGame : MazeGame{
private Spell m_spell;
public EnchantedMazeGame(Spell spell){
this.m_spell = spell;
}
public override Door MakeDoor(Room r1,Room r2){
return new EnchantedDoor(r1,r2);
}
public override Room MakeRoom(int n){
return new EnchantedRoom(n,this.m_spell);
}
}
}
上面的代码在.NET IDE里测试通过,上面说过了在FCL里面又很多的工厂方法的实现,如果想要继续学习和理解可以看看MSDN的帮助。希望我写的这些可以帮助你更好的理解工厂方法模式。如果有什么不正确的地方希望指出可以发邮件到wu_jian830@hotmail.com里,我将虚心学习,希望我们可以共同进步。
相关文章:
美国AI博士指出,自学Python到底能做什么
我见过市面上很多的 Python 讲解教程和书籍,他们大都这样讲 Python 的:先从 Python 的发展历史开始,介绍 Python 的基本语法规则,Python 的 list, dict, tuple 等数据结构,然后再介绍字符串处理和正则表达式࿰…

关于微博溯源的后续问题
1、在进行关键词搜索的时候,如何分词,我们不可能用语料库进行匹配,已没有语料可以学习。 2、关于转折点的寻找。目前我们使用高级搜索,从当前时间往前推,根据搜索到微博的条数变化,确定时间发生具体时间&am…

python3的数据类型以及模块的含义
python3的数据类型以及模块的含义购物车转载于:https://blog.51cto.com/11834445/1884901
设计模式之C#实现---- ProtoType
作者: cuike519的专栏 http://blog.csdn.net/cuike519/该模式的意图是:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。那么首先我们应该已经有了一个对象,同时这个对象还支持自我复制(科隆&…

快速排序(二)最后修改
1 //2012-07-162 void quickSort(element list[], int left, int right)//快速排序3 {4 int ileft;5 int jright;6 7 if(i > j) //判断需要i<j8 return;9 10 element templist[i]; 11 12 while(i<j) 13 { 14 while(i<j …
性能超越GPU、FPGA,华人学者提出软件算法架构加速AI实时化
作者 | 王言治,美国东北大学电子与计算机工程系助理教授出品 | AI科技大本营(ID:rgznai100)近年来,机器学习(Machine Learning)领域的研究和发展可谓是与日俱新,各式各样与机器学习相关的研究成果与应用层出不穷&#…

PHP获取毫秒时间戳,利用microtime()函数
PHP获取毫秒时间戳,利用microtime()函数 php本身没有提供返回毫秒数的函数,但提供了一个microtime()函数,借助此函数,可以很容易定义一个返回毫秒数的函数。php的毫秒是没有默认函数的,但提供了一个microtime()函数&am…

.NET中添加控件数组
作者:cuike519的专栏 http://blog.csdn.net/cuike519/添加控件数组 在.NET里面我好像没有找到有关于控件数组的说明,但是前两天偶在网上看到了一篇关于如何在.NET里面实现控件数组的文章(该文章请参看MSDN).记得大学的时候在使用VB的时候使用过控件数组,可是到了…
如何在机器学习的框架里实现隐私保护?
编者按:数据时代,人们从技术中获取便利的同时,也面临着隐私泄露的风险。微软倡导负责任的人工智能,因此机器学习中的隐私保护问题至关重要。本文介绍了目前机器学习中隐私保护领域的最新研究进展,讨论了机密计算、模型…

函数图像轻松画:教你用永中图象
函数图像轻松画:教你用永中图象 函数图像轻松画:教你用永中图象转载于:https://blog.51cto.com/premium/933220

c语言语系的命名风格和java系命名风格
c语言系的命名风格:单词之间使用下划线分隔。如上图。 java语言是另外一个系,javascript属于java语系(当年就是想借助java的名气所以命名javascript)。java语系是驼峰式命名法,如getElementById()。如果使用c语系命名风格则使用下划线分隔 ge…

全国IP地址分配表
xa.sn.cn,西安公众网,西安,陕西,CN,202.100.0.* xa.sn.cn,西安公众网,西安,陕西,CN,202.100.1.* xa.sn.cn,西安公众网,西安,陕西,CN,202.100.2.* xa.sn.cn,西安公众网,西安,陕西,CN,202.100.3.* xa.sn.cn,西安公众网,西安,陕西,CN,202.100.4.* xa.sn.cn,西安公众网,西安,陕西,C…
神同步!美国三地 Tesla 车主,自动驾驶都撞了警车
来源 | HyperAI超神经(ID:HyperAI)内容概要:上周在美国北卡州发生了一起交通事故,一辆自动驾驶模式下的 Tesla 撞击了停靠在路边的警车,虽未造成人员伤亡,但车辆损毁严重。事故调查中发现&#…

Q币才是腾讯真正的世界级产品
本文受《虚拟货币将是下一个大平台》启发而来。何玺认为,腾讯Q币本身就具有全球化虚拟货币的基因。 日前,有媒体报道了Pocket Change获得了由Google Ventures领投的500万美元A轮融资,使其融资总额达到640万美元。 Pocket Change是一个为Andro…

解决Office互操作错误检索COML类工厂中 CLSID为 {xxx}的组件时失败,原因是出现以下错误: 80070005...
Excel为例(其他如Word也适用)文件数据导入时报出以下错误: 检索COML类工厂中 CLSID为 {00024500-0000-0000-C000-000000000046}的组件时失败,原因是出现以下错误: 80070005,如图所示: 可以看到报出的异常类型为:UnauthorizedAcces…

再论制硬盘逻辑锁
姜卓睿 雷必武 一、序言 由于教学工作需要,本人在参看了贵刊98年第4期《硬盘逻辑锁技术研究及应用》与99年第3期《解开硬盘逻辑死锁的一种有效方法》的文章之后,决定以同类方法尝试一下,结果未获得成功,又“苦于”没有KV300 L …
我国科学家成功研制全球神经元规模最大的类脑计算机
来源 | 之江实验室(ID:zhejianglab)9月1日,浙江大学与之江实验室举办成果发布会,共同发布我国首台基于自主知识产权类脑芯片的类脑计算机(Darwin Mouse)。浙江大学校长吴朝晖院士出席并讲话。他…

批处理获取目录下所有文件名
由于要处理一些文件,找了个这样的批处理: 输出目录及子目录下所有的jpg图片的文件名,不含扩展名 1 echo off 2 cd.>List.txt 3 for /f "delims" %%i in (dir /s/a-d /b *.jpg) do >>List.txt echo %%~ni>>JustName.…

1001: 整数求和
描述:求两个整数之和输入:输入数据只包括两个整数A和B。输出:两个整数的和。样例输入:1 2样例输出:3考点:运算符代码: #include <stdio.h> int main() {int a,b;int c;scanf("%d",&a);scanf("%d",&b);cab;printf("%d",…

ASP.NET 2.0 中的新增安全功能
发布日期: 8/26/2004| 更新日期: 8/26/2004Stephen Walther Microsoft Corporation 适用于: Microsoft ASP.NET 2.0 Microsoft ASP.NET framework Microsoft SQL Server Microsoft Visual Studio .NET 摘要:ASP.NET 2.0 包含一些新…
GitHub 标星 20000+,国产 AI 开源从算法开始突破 | 专访商汤联合创始人林达华
作者 | 阿司匹林责编 | 李雪敬封图 | CSDN 下载自视觉中国作为已经有4000多名员工的AI独角兽,商汤的一举一动备受关注。从2018年开始,奔着“开源、统一、可复现”的目标,商汤开始建设人工智能算法的开源体系。当时,商汤联合创始人…

那些年,我们一起学过的汇编----之伪指令
弄懂了前面几篇关于基础的文章,下面就开始我们真正的汇编之旅了,在这一篇中我们着重来强调下汇编语言的伪指令。伪指令是汇编语言程序设计中的一个主要的部分,属于控制命令,在汇编语言中的数据定义、存储单元分配、指示程序结果等…

JavaScript-数据引用类型对象
1 <!DOCTYPE html>2 <html>3 <head lang"en">4 <meta charset"UTF-8">5 <title></title>6 </head>7 <body>8 <script>9 //按值传递:两个变量间赋值时,或将变量作为参数传入函数时,其实…
热点 | Excel不“香”了,数据分析首选Pyhton!
Excel一直在求职中有着不可动摇的地位无论是投行、咨询、四大曾经都会在JD中明确要求会Excel,而Excel称霸的时代已经过去!事实上,为了追求更高的效率和质量,他们开始使用比Excel更高效的Python,随后交易收入增长了15%。…

ASP.NET中实现打印
怎样才可以调用打印机进行打印并且对纸张类型进行设置呢? --------------------------------------------------------------- <OBJECT id"WebBrowser" height"0" width"0" classid"CLSID:8856F961-340A-11D0-A96B-00…
you have new email in /var/spool/mail/root/
有时在进入系统的时候经常提示You have new mail in /var/spool/mail/root 解决方法:修改系统配置文件/etc/profile,告诉系统不要去检查邮箱. 具体操作: 命令行输入:echo "unset MAILCHECK" >> /etc/profile 【把…

写时复制,写时拷贝,写时分裂,Copy on write
2019独角兽企业重金招聘Python工程师标准>>> 写时复制,写时拷贝,写时分裂 (Copy-on-write,简称COW)是计算机资源管理方面的一种优化技术,有着广泛的应用,比如内存管理(进…

C#生成pdf的源代码
作者:qieyj(温馨港湾) http://search.csdn.net/Expert/topic/1256/1256076.xml?temp.1866419//write by wenhui.orgusing System;using System.IO;using System.Text;using System.Collections; namespace PDFGenerator{ public class PDFGenerator{static fl…
迁移性好、多用途,港中文提出特征分离的无监督人类三维姿态表征
来源 | 我爱计算机视觉(ID:aicvml)本文将介绍一种基于特征分离的通用人类姿态特征的学习算法Unsupervised Human 3D Pose Representation with Viewpoint and Pose Disentanglement。该算法从无监督的特征分离过程中,习得了一个迁移性好、多用…

解決Linux下Android开发真机调试设备不被识别问题
为什么80%的码农都做不了架构师?>>> 在google找了不少关于这个的资料,各种添加和修改系统文件,但是我的defy依旧没有被识别。尼马的! 好吧,是我低估了Android的sdk的adb调试工具,其实简单的两个…