文档章节

设计模式随笔-从“有病”说起(工厂模式前传)

Start-up
 Start-up
发布于 2012/05/17 17:29
字数 1447
阅读 59
收藏 0

十年前,我有一个很有钱的朋友,他家有三辆汽车(VOLVO(沃尔沃)、BENCH(奔驰)、MAZDA(马自达)),还雇了司机为他开车。不过, 这个人上车后跟司机说的话取决于他坐的车:当他坐上VOLVO后,会跟司机说“开沃尔沃车!”,坐上BENCH后他说“开奔驰车!”,坐上MAZDA后他 说“开马自达车!”。

大家猜这个人怎么着?.....有病!

其实我这个朋友叫“C”。

注:我对C一直很虔诚,上大学时,C语言是我最喜爱的语言。而且它的功能要远比我在后面例子中描述的功能强大的多(毕竟还有杀手锏“指针”呢),我并不想让这段故事给C留下什么不好的印象,只是举例而已(BASIC和VFP什么的连举例资格都没有呢  )。

把上面的故事用C写下来的化就是(我在Tubro C++ 1.0下调试通过):

/****** CARTEST.C *******/

#include
< stdio.h >

void  PrintHelp();
void  DriveVolvo();
void  DriveBench();
void  DriveMazda();

main(
int  argc,  char   * argvs[])
{
  
if(argc < 2)
  
{
    PrintHelp();
    
return;
  }

  
  
// C 先生的开车法则
  if(strcmp(argvs[1],"V")==0)
    DriveVolvo();     
//开沃尔沃车!
  else if(strcmp(argvs[1], "B")==0)
    DriveBench();     
//开奔驰车!
  else if(strcmp(argvs[1], "M")==0)
    DriveMazda();     
//开马自达车!
  else
    PrintHelp();

  
return;
}


void  PrintHelp()
{
  printf(
"Please input a correct car type.(V, B, M) ");
}


void  DriveVolvo()
{
  printf(
"Driving Volvo  ");
}


void  DriveBench()
{
  printf(
"Driving Bench  ");
}

void  DriveMazda()
{
  printf(
"Driving Mazda  ");
}

程序编译成可执行文件后,在命令提示符下输入: CARTEST V 或 CARTEST B 或 CARTEST M。程序自动完成开不同车的功能。

现在让我们看看C先生病在哪里?其实,C先生之所以“有病”,就是在他发号的施令上,实际上只要说声“开车”就行了,他却不厌其烦的在里面加上车名(DriveVolvo(); DriveBench(); DriveMazda();)。

如果用用C#改写上面的程序的话,我们可以将程序写成:

using  System;

public   class  Client
{
  
public static void Main(string[] argvs)
  
{
    Car c;

    
if(argvs.Length < 1)
    
{
      PrintHelp();
      
return;
    }


    
// 司机将车开来
    if(argvs[0== "V")
      c 
= new Volvo();
    
else if(argvs[0== "B")
      c 
= new Bench();
    
else if(argvs[0== "M")
      c 
= new Mazda();
    
else
    
{
      PrintHelp();
      
return;
    }


    
// C#先生发号施令“开车!”
    c.Drive();
  }


  
private static void PrintHelp()
  
{
    Console.WriteLine(
"Please input a correct car type.(V, B, M)");
    Console.WriteLine(
"For example: CarTest M");
  }

}


public   abstract   class  Car
{
  
public abstract void Drive();
}


public   class  Volvo : Car
{
  
public override void Drive()
  
{
    Console.WriteLine(
"Driving Volvo ");
  }

}


public   class  Bench : Car
{
  
public override void Drive()
  
{
    Console.WriteLine(
"Driving Bench ");
  }

}


public   class  Mazda : Car
{
  
public override void Drive()
  
{
    Console.WriteLine(
"Driving Mazda ");
  }

}

现在问题就出来了,这两种做法哪种更好一些呢?是不是C#将本是很简单的问题搞复杂了呢?让我们分析一下:

  1. 从代码长度来看,显然C语言的代码长度要远少于C#的代码。两程序完成的是相同的功能。
  2. 从代码结构上看,C语言的结构也要比C#清晰,属于典型的结构化程序设计。
  3. 从“有病”的角度看,显然C语言程序“有病”,而C#程序更为容易接受。

C#程序通过对车的抽象,实现只需“开车”,就可以调用任何车的开车方法。这就是我们常说的“多态性”。将多态性浓缩到两行代码上,就是(以下简称方法一):

Car c  =   new  Bench();
c.Drive();

不要小看这两行代码,隐藏在其中的深意还需要我们好好挖掘一下。

有人可能会问,不就是开车吗,开奔驰就是开奔驰,干吗要把奔驰赋值给车,然后调用车的开车,再通过多态性转而调用奔驰的开车。如此麻烦,不如直接就调用奔驰的开车(以下简称方法二):

Bench b  =   new  Bench();
b.Drive();

到底谁好谁坏,我们可以从两个角度来看这个问题:

一、从迪米特法则的角度来看:

(关于迪米特法则,请参考:C#设计模式(3)

迪米特法则可以简单的表述成最小知识原则,也叫做“使民无知”。一个对象应当对其它对象知道的越少越好。

如果客户在进行代码调用时,使用了方法二的方法,那么当不开奔驰转开沃尔沃时,必须将客户端所有Bench的代码改为Volvo。如果两个车都可能开的化,那么客户端不得不跟两个对象都打交道。

如果采用方法一的方法,客户只需要知道“车”就行了,反正车都可以开。至于什么车,客户并不关心,关键的是能开就行。这不但很好的应用了迪米特法则,同时也应用了里氏代换原则(参见:C#设计模式(2)):“一个子类可以替换掉父类”。这允许在客户不知情的情况下就可以代换不同类型的车。

二、从开放封闭原则的角度来看:

(关于开放封闭原则,请参考:C#设计模式(2)

开 放封闭原则要求对修改封闭,对扩展开放。在上面的两个例子种,方法二没有很好遵循开放封闭原则,当添加新类型汽车后,不得不修改代码以适应这种改变。而方 法一具有很强的适应性,只需要给Car对象添加一个子类就可以了,客户由于只知道有“车”,所以加一种新车后,根本不需要改变客户端代码。因此也提高了系 统的可维护性。

工厂模式中之所以引入“工厂”的概念,而抛弃直接使用 new 实例化对象,其中一个根本的原因也在于此。通过对“ 简单工厂模式”、“ 工厂方法模式”以及“ 抽象工厂模式”的学习我们会很强的感受到这点。

© 著作权归作者所有

Start-up
粉丝 46
博文 292
码字总数 579103
作品 0
海淀
高级程序员
私信 提问
简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别

转载:原地址http://www.cnblogs.com/zhangchenliang/p/3700820.html 简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别 结合简单示例和UML图,讲解工厂模式简单原理。 一、引子 话说...

法斗斗
2018/05/08
277
0
设计模式梳理(一)

设计模式梳理(一) 总体来说设计模式分为三大类: @案例源码地址:https://gitlab.com/lxqxsyu/DisgnPattern 创建型模式 简单工厂模式 工厂类是整个模式的关键。它包含必要的判断逻辑,能够...

lxq_xsyu
2017/11/02
0
0
Java设计模式系列一(前言)

说起设计模式,很多人都会觉得这个概念很熟悉,会想到单例模式、工厂模式等等,但是似乎又说不出来什么,说不上它的核心思想和设计原则。其实我们的项目中,为了代码复用,增加可维护性,很多...

Mooree
04/07
27
0
【设计模式笔记】(十六)- 代理模式

一、简述 代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。 其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中...

MrTrying
2018/06/24
0
0
炒冷饭系列:设计模式 抽象工厂模式

炒冷饭系列:设计模式 抽象工厂模式 摘要: 原创出处: http://www.cnblogs.com/Alandre/ 泥沙砖瓦浆木匠 希望转载,保留摘要,谢谢! 亲爱我,孝何难;亲恶我,孝方贤。 一、什么是抽象工厂模...

泥沙砖瓦浆木匠
2014/07/24
160
0

没有更多内容

加载失败,请刷新页面

加载更多

基于k8s的Ingress部署hexo博客(http和https)

注:kuberntes版本为1.15 什么是 Ingress Ingress 是一个提供对外服务的路由和负载均衡器,其本质是个nginx控制器服务。 k8s文档上Ingress经典数据链路图: internet | [ In...

Kanonpy
18分钟前
5
0
LNMP---访问控制

访问控制 扩展: curl命令用法: curl -v -A 'aaaaaspider/3.0' -e "1111" -x127.0.0.1:80 discuz.tobe.com -I -A 指定user-agent -e 指定referer -x 指定访问目标服务器的ip和por......

tobej
25分钟前
5
0
Python实现合并排序(归并排序)(一文看懂)

1、归并排序原理 归并排序采用分而治之的原理: 一、将一个序列从中间位置分成两个序列; 二、在将这两个子序列按照第一步继续二分下去; 三、直到所有子序列的长度都为1,也就是不可以再二分...

onedotdot
30分钟前
3
0
linux查询日志命令总结

【背景】 排查线上环境问题,少不了去线上查日志。而使用什么命令,能快速准确地查到我们需要查找地日志信息,也是我们需要掌握的一项技能。 【命令】 Linux查看命令有多种:tail,head,cat...

chen-chen-chen
49分钟前
11
0
net/http 接收文件

代码展示,如何使用golang 自带net/http,将Form表单中提交上来的文件,指定位置保存。 ReadHtmlFile OutHtml(html网页,表单测试代码使用) SaveFile (处理提交文件) package mainimport...

听夜深窗外风
54分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部