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

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

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

其实我这个朋友叫“C”

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

把上面的故事用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 实例化对象,其中一个根本的原因也在于此。通过对简单工厂模式工厂方法模式以及抽象工厂模式的学习我们会很强的感受到这点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值