简单工厂模式,工厂方法模式,抽象工厂模式,spring的狂想
菜鸟D在项目中遇见一个比较纠结的高耦合,所以就想办法来解耦。情况是这样的:系统通过用户选择treeview控件的节点判断调用不同的处理,这些处理中某些东西又是类似的。同事的建议是采用简单工厂,耦合就耦合吧,反正treeview节点基本是不会变化的。(能偷懒就偷懒吧)
菜鸟D有些偏执,想找些方法来解耦。于是就学习了这个几种方法,虽然不一定用的上,多学一点总是好的。
首先说简单工厂,例子是一个已经二到死的计算器。
简单工厂由三种角色组成:工厂类角色(creator),抽象产品类角色(product,图中为Iproduct接口),具体产品类角色(concreteproduct,图中为Product_A…)。

代码如下:
public class Calculate
{
public double DValue1 { get; set; }
public double DValue2 { get; set; } public virtual double GetResult()
{
return ;
}
} public class Add : Calculate
{
public override double GetResult()
{
return DValue1 + DValue2;
}
} public class Sub : Calculate
{
public override double GetResult()
{
return DValue1 - DValue2;
}
} public class Mul : Calculate
{
public override double GetResult()
{
return DValue1 * DValue2;
}
} public class Div : Calculate
{
public override double GetResult()
{
if (DValue2.Equals())
{
if (DValue1.Equals())
{
return ;
}
return ;
}
return DValue1 / DValue2;
}
}
基本的代码
#region 客户端
Console.WriteLine("请输入第一个数");
string v1 = Console.ReadLine();
Console.WriteLine("请输入运算符");
string oper = Console.ReadLine();
Console.WriteLine("请输入第二个数");
string v2 = Console.ReadLine();
Calculate c = GetCalculateByOper(oper);
c.DValue1 = Convert.ToDouble(v1);
c.DValue2 = Convert.ToDouble(v2);
Console.WriteLine("结果:" + c.GetResult());
#endregion #region 工厂方法
private static Calculate GetCalculateByOper(string oper)
{
Calculate c = new Calculate();
switch (oper)
{
case "+": c = new Add();
break;
case "-": c = new Sub();
break;
case "*": c = new Mul();
break;
case "/": c = new Div();
break;
}
return c;
}
#endregion
在这个例子中没有使用抽象类,接口,而是用了虚方法(纯粹是不想在接口里定义属性,自己喜欢用什么就用什么,毕竟这不是简单工厂的核心)。简单工厂的核心是充当工厂类角色的工厂方法(起码在此例中是这样),逻辑判断中使具体产品类和工厂直接耦合了(好像类图的关系已经决定了这种耦合,但是将具体产品和客户端解耦)。如果要添加一个开方运算,首先添加开方的运算类,然后打开工厂方法进行修改。(呵呵,违反了开闭原则)
接下来再说一下工厂方法模式,类图如下:

先分析一下类图,工厂模式有了四个角色:工厂接口,工厂实现,产品接口,产品实现(其实说角色不贴切,还不如说是种类)。工厂实现和产品实现有了依赖关系,通过调用工厂实现来获取产品实现。也就是一种产品实现对应一种工厂实现。
还是那个二的要死的计算器的例子,代码如下:
public interface IFactory
{
ICalculatable GetCalculatable();
} public interface ICalculatable
{
double GetReslut(double d1, double d2);
} public class Add : ICalculatable
{
public double GetReslut(double d1, double d2)
{
return d1 + d2;
}
} public class AddFactory : IFactory
{
public ICalculatable GetCalculatable()
{
return new Add();
}
} public class Sub : ICalculatable
{
public double GetReslut(double d1, double d2)
{
return d1 - d2;
}
} public class SubFactory : IFactory
{
public ICalculatable GetCalculatable()
{
return new Sub();
}
} public class Mul : ICalculatable
{
public double GetReslut(double d1, double d2)
{
return d1 * d2;
}
} public class MulFactory : IFactory
{
public ICalculatable GetCalculatable()
{
return new Mul();
}
} public class Div : ICalculatable
{
public double GetReslut(double d1, double d2)
{
if (d2.Equals())
{
if (d1.Equals())
{
return ;
}
return ;
}
return d1 / d2;
}
} public class DivFactory : IFactory
{
public ICalculatable GetCalculatable()
{
return new Div();
}
}
基本代码
#region 逻辑判断
public class Factory
{
public static IFactory GetCalculatableByOper(string oper)
{
IFactory factory = null;
switch (oper)
{
case "+": factory = new AddFactory();
break;
case "-": factory = new SubFactory();
break;
case "*": factory = new MulFactory();
break;
case "/": factory = new DivFactory();
break;
}
return factory == null ? null : factory;
}
}
#endregion #region 客户端
Console.WriteLine("请输入第一个数");
string v1 = Console.ReadLine();
Console.WriteLine("请输入运算符");
string oper = Console.ReadLine();
Console.WriteLine("请输入第二个数");
string v2 = Console.ReadLine();
ICalculatable calculatable = Factory.GetCalculatableByOper(oper).GetCalculatable();
Console.WriteLine("结果:" + calculatable.GetReslut(Convert.ToDouble(v1), Convert.ToDouble(v2)));
#endregion
注意:此处的判断逻辑已经不属于工厂模式了(类图中很明确),也可以脱离客户端。虽说简单工厂也可以这样将判断逻辑脱离客户端,但是简单工厂的逻辑判断是在工厂类中的,也就依然是简单工厂模式的一部分(此处逻辑有些乱,不懂的就认真研究类图)。通常在工厂模式的客户端需要用到哪种工厂再去创建哪种工厂,而不是如例子中的使用一个判断逻辑来选择,也就是工厂模式创建工厂是通过“外部”的逻辑来确定,而模式本身是不会做判断的。(所以这个例子并不是一个合适的例子)
此时,计算器要再添加一个开方的方法,只需添加方法实现,添加工厂实现,不会对原有的代码做修改,符合了开闭原则(你当我瞎啊,你不修改判断逻辑吗?说了多少次了,判断逻辑部分不属于工厂模式。不过比简单工厂确实多写不少东西)。
工厂方法模式通常在以下两种情况中使用 :
第一,需要使用某种产品,客户端很清楚应该使用哪个具体工厂,此种情况不存在上述的判断逻辑,只需要实例化具体工厂,然后生产具体产品。
第二,需要使用某种产品,但是不想也不需要知道是哪个工厂创建,此种情况存在一定的判断逻辑,但是客户端不需要知道这个逻辑,生产方(工厂模式)是通过外部的逻辑来生产产品,这时由外部来实例化具体工厂,生产具体产品交付给客户端。
接着再来说说抽象工厂,在抽象工厂中最明显的一个特点是产品不止一类了,所以前文提到的计算机的例子就不适合使用抽象工厂模式(窃以为如此,有反对意见的欢迎提出)。
在抽象工厂在提到一个“产品族”的概念,其实在抽象工厂中最为直观的体现就是产品A接口,产品B接口甚至是产品C、D接口。类图如下,从类图中不难发现,抽象工厂模式的具体工厂角色可以生产多种产品(工厂方法模式的具体工厂只能生产一种产品,不信去看类图)。就好像东方红拖拉机厂在工厂方法模式下只能生产拖拉机,但是在抽象工厂模式下不仅能生产拖拉机,还能生产卡车(如果加一个坦克的生产线,还能生产坦克)。

此处就用比较熟悉的农场的例子,农场生产水果和蔬菜,水果分为北方水果和热带水果,蔬菜也一样,所以农场也分为北方农场和热带农场。
代码如下:
public interface IFruit
{
string Show();
} public interface IVeggie
{
string Show();
} public class NFruit : IFruit
{
private string name;
public string Name
{
get { return "北方" + name; }
set { name = value; }
} public string Show()
{
return Name;
}
} public class TFruit : IFruit
{
string name; public string Name
{
get { return "热带" + name; }
set { name = value; }
} public string Show()
{
return Name;
}
} public class NVeggie : IVeggie
{
string name; public string Name
{
get { return "北方" + name; }
set { name = value; }
} public string Show()
{
return Name;
}
} public class TVeggie : IVeggie
{
string name; public string Name
{
get { return "热带" + name; }
set { name = value; }
} public string Show()
{
return Name;
}
} public interface Factory
{
IFruit CreateFruit(string nm);
IVeggie CreateVeggie(string nm);
} public class NFactory : Factory
{
public IFruit CreateFruit(string nm)
{
return new NFruit() { Name = nm };
} public IVeggie CreateVeggie(string nm)
{
return new NVeggie() { Name = nm };
}
} public class TFactory : Factory
{
public IFruit CreateFruit(string nm)
{
return new TFruit() { Name = nm };
} public IVeggie CreateVeggie(string nm)
{
return new TVeggie() { Name = nm };
}
}
基本代码
此例的逻辑判断部分没写。从类图上看,它是不属于抽象工厂模式的,从简化客户端的角度,这个逻辑判断是可以从客户端剥离的,很显然和工厂方法模式的逻辑判断属于同一地位(有种两头受气的感觉)。
然后再简单说spring的依赖注入。在spring中需要获取一个产品(实例)如何获取?容器来提供。逻辑判断呢?可以放在客户端,也可以从客户端剥离,爱放哪放哪。容器只依赖于配置,逻辑判断不会影响容器。所以容器很好的将生产过程与客户端隔离,这就不存在耦合了。
接下来再说一下,一直不停的重复的判断逻辑。在菜鸟D 的看法里,上述几个模式的耦合都是存在于判断逻辑中的。在简单工厂中,工厂和客户端之间耦合较低,但是工厂和具体的产品类是直接耦合的。在工厂方法模式、抽象工厂模式、spring中,逻辑判断只是一个辅助,逻辑判断将生产过程和客户端隔离,大大地降低了耦合程度。所以在衡量设计模式的耦合时,就需要衡量判断逻辑在模式中作用和角色。有些耦合是很难避免的,为了避免这些耦合甚至可能会造成更多的耦合。至于开闭原则,编码过程尽量去注意,否则为以后的开发带来麻烦就不是我们想要的了。
一家之言,不值一哂,如有谬误,欢迎指正。
菜鸟D希望这篇文章对您有所帮助。
扩展参考:
大话设计模式P72:
工厂方法实现时,客户端需要决定实例化哪一个工厂来实现运算类(具体产品),选择判断的问题还是存在的,也就是说,工厂方法吧简单工厂的内部逻辑判断移到了客户端代码来进行.想要添加功能,本来是要改工厂类的,而现在是修改客户端.
三种工厂模式区别总结
http://blog.csdn.net/lingfengtengfei/article/details/12374469
spring.net的依赖注入
http://www.cnblogs.com/GoodHelper/archive/2009/10/26/SpringNET_DI.html
工厂方法模式
http://blog.csdn.net/zhengzhb/article/details/7348707
三种工厂模式
http://www.cnblogs.com/poissonnotes/archive/2010/12/01/1893871.html
简单工厂模式,工厂方法模式,抽象工厂模式,spring的狂想的更多相关文章
- Java设计模式---工厂模式(简单工厂、工厂方法、抽象工厂)
工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类.工厂模式的形态工厂模式主要用一下几种形态:1:简单工厂(Simple Factory).2:工厂方法(Factory M ...
- 设计模式(Python)-简单工厂,工厂方法和抽象工厂模式
本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题: 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的 ...
- 设计模式3---工厂模式(Factory Pattern简单工厂、工厂方法、抽象工厂)
工厂模式:主要用来实例化有共同接口的类,工厂模式可以动态决定应该实例化那一个类.工厂模式的形态工厂模式主要用一下几种形态:1:简单工厂(Simple Factory).2:工厂方法(Factory M ...
- Java设计模式之简单工厂、工厂方法和抽象工厂
在前面的学习中(参见前面的博客),我们学到了很多OO原则: 封装变化 多用组合,少用继承 针对接口/超类编程,不针对实现编程 松耦合 开闭原则 让我们从一个简单的类开始,看看如何将之改造成符合OO原则 ...
- 简单工厂 VS 工厂方法 VS 抽象工厂
说到设计模式.自然少不了简单工厂模式.工厂方法和抽象工厂这三姐妹. 它们之间可谓是各有所长,术业专攻啊!这篇博客来简单的梳理一下三者之间的关系. 那么工厂又是什么意思呢?结合三者的特点,我觉得能够这样 ...
- headfirst设计模式(5)—工厂模式体系分析及抽象工厂模式
先编一个这么久不写的理由 上周我终于鼓起勇气翻开了headfirst设计模式这本书,看看自己下一个设计模式要写个啥,然后,我终于知道我为啥这么久都没写设计模式了,headfirst的这个抽象工厂模式, ...
- c# 设计模式 之:简单工厂、工厂方法、抽象工厂之小结、区别
很多时候,我发现这三种设计模式难以区分,常常会张冠李戴闹了笑话.很有必要深入总结一下三种设计模式的特点.相同之处和不同之处. 1 本质 三个设计模式名字中都含有“工厂”二字,其含义是使用工厂(一个或一 ...
- Java 工厂模式(一)— 抽象工厂(Abstract Factory)模式
一.抽象工厂模式介绍: 1.什么是抽象工厂模式: 抽象工厂模式是所有形态的工厂模式中最为抽象和最具有一般性的一种形态,抽象工厂模式向客户端提供一个接口,使得客户端在不知道具体产品的情类型的情况下,创建 ...
- head first 设计模式笔记4-工厂模式(简单工厂、工厂方法、抽象工厂)
1.简单工厂模式 Pizza public abstract class Pizza { abstract void prepare(); abstract void bake(); abstract ...
随机推荐
- SpringBoot JPA实现增删改查、分页、排序、事务操作等功能
今天给大家介绍一下SpringBoot中JPA的一些常用操作,例如:增删改查.分页.排序.事务操作等功能.下面先来介绍一下JPA中一些常用的查询操作: //And --- 等价于 SQL 中的 and ...
- SVN 备忘录
上传文件夹 svn import distcomp/ svn+ssh://USERNAME@166.120.110.119/mnt/disk1/fserver/svn/distcomp -m &quo ...
- iOS 界面 之 EALayout 无需反复编译,可视化实时界面,告别Storyboard AutoLayout Xib等等烦人的工具
http://blog.csdn.net/fatherhui iOS开发,EALayout 无需反复编译,可视化实时界面,告别Storyboard AutoLayout Xib等等烦人的工具 EALa ...
- 将MPLS编译进linux内核中
系统环境:linux kernel 2.6.35.(此环境是上一篇文章中将ubuntu内核替换后的环境) 编译过程如下: 1)首先需要下载patch文件:linux-kernel-v2.6.35-mp ...
- ajax 跨域了 cors
<?php /** * Author: humanhuang * Date: 13-12-17 */ header('Access-Control-Allow-Origin:*'); heade ...
- 1.3. 创建 Grocery Dude 项目(Core Data 应用程序实践指南)
该程序功能如下: 分类显示家里各个位置的东西,以此提示该购买哪些生活用品 在超市购买时,告诉某个货品摆在哪条过道旁的货架上 将待买物品按过道编组,这样每个过道只需走一遍,就可以拿完所需货品 通过iCl ...
- java程序的工作原理
Sun公司设计java语言的目标是让Java程序不必经过修改就可以在各种各样的计算机(包括PC机和工作站)上运行.为了实现这一目标,Sun公司提供了一阵Java虚拟机(Java Virtual Mac ...
- oracle求时间差的常用函数
oracle求时间差的常用函数 求时间差: 天: ROUND(TO_NUMBER(END_DATE - START_DATE)) 小时: ROUND(TO_NUMBER(END_D ...
- border-radius是向元素添加圆角边框的方法
border-radius:10px; /* 所有角都使用半径为10px的圆角 */ border-radius: 5px 4px 3px 2px; /* 四个半径值分别是左上角.右上角.右下角和左下 ...
- Bootstrap入门(二十二)组件16:列表组
Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...