imjacob的专栏

首页博文目录订阅
正 文

步步为营,重构出模式[转]

(2009-1-11 15:29)

公司每年都招聘很多刚毕业的新员工,在实习的时候,每每有人向他们推荐代码大全、设计模式这两本书的时候,我都劝诫他们,代码大全是很好的书,设计模式你们看还太早,没有几万行的代码经验,你们是看不明白滴。结果还是这样的结果:看了设计模式后,只是知道概念,却不知道如何运用,貌似理解,实则不然。我就打算写一个例子,把经常会见到的模式都能体现出来,就从今天做起吧。
首先声明一下,这些文章只对新手有帮助,高手可以帮帮我改进改进,呵呵。

假设这么一个需求:一个控制台程序,需要根据用户输入的指令进行不同的处理,比如,有echo、exit,那么,我下笔就写下了下面的代码:

Code

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->command = Console.ReadLine();
if (command.IndexOf("echo") == 0)
{
    Console.WriteLine(command.Substring(4, command.Length - 4));
}
else if (command.IndexOf("exit") == 0)
{
    System.Environment.Exit(-1);
}
else
{
    Console.WriteLine("Unknown Command:" + command);
}

这样的代码恰好就被一个高手看到了,高手对我说:你这样不行啊,你把对用户输入指令的接收、解析和执行统统放到一起了,耦合度太高,你需要重构,把每个不同的职责分开,不要搞得一团浆糊。
我一想,对啊,是挺乱的,怎么改呢?
对,我先把对指令的执行抽出来,单做成一个类,给它做一个方法:execute,让这个方法执行具体的动作。
过了一会儿,代码出来了,已经是如下模样了:

Code

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->command = Console.ReadLine();
if (command.IndexOf("echo") == 0)
{
    new EchoCommand(command.Substring(4, command.Length - 4)).execute();
}
else if (command.IndexOf("exit") == 0)
{
    new ExitCommand().execute();
}
else
{
    new UnknownCommand(command).execute();
}

做成了三个类,EchoCommand、ExitCommand、UnknownCommand,虽然多出来三个类的代码,但是主程序看起来比以前工整多了,顺眼多了。
这个时候,高手过来看我的成果,面有喜色,说:小伙儿,不过啊,这俨然是Command模式雏形了。
啥,这就是模式了?
高手就开始说教:只是雏形而已,还有很多要做的,比如,应该对用户指令进行统一调度,三个Command类为什么不是用接口?等等等等。
我就犯愁了,这我也搞不定啊。
高手坐下来,奋笔疾书。在之前的代码基础上,加了一个接口ICommand,让三个Command类实现这个接口;加了个Processor类,对指令统一管理,最终代码如下:

Code

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->String command = "";
Processor p = new Processor();
p.addCommand("Unknown", new UnknownCommand());
p.addCommand("Exit", new ExitCommand());
p.addCommand("Echo", new EchoCommand());
while (true)
{
    command = Console.ReadLine();
    if (command.IndexOf("echo") == 0)
    {
        p.resetRequest("Echo", command.Substring(4, command.Length - 4));
        p.request("Echo");
    }
    else if (command.IndexOf("exit") == 0)
    {
        p.resetRequest("Exit", command);
        p.request("Exit");
    }
    else
    {
        p.resetRequest("Unknown", command);
        p.request("Unknown");
    }
}

写完之后,高手喘了口气,说,现在的代码风格不好,你还需要重构一下。
我想,这次重构也就是把字符串定义为常量了吧。
高手很严肃地说:今天快下班就不说了,明天下午我CodeReview的时候不希望看到这么多的字符串。
啊?!这该怎么办??????

(代码:http://www.cnblogs.com/Files/game-over/Command.rar)

上回说到Command的雏形被高手改过之后,高手让我再对代码进行一下重构和完善,并且还特意嘱咐不要出现那么多的字符串。饿滴神啊,该怎么办呢?
外事不决问Google,果然,有办法了:
我用一个配置文件,把用户输入的命令和实际的Command类一一对应,然后利用反射实例化这个类。
好,那就开始做吧。
首先创建app.config文件,这个是工程默认会读取的,就不用自己解析XML了,这个文件里面是这样的:

Code

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--><?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="echo" value="Command.EchoCommand"/>
    <add key="exit" value="Command.ExitCommand"/>
  </appSettings>
</configuration>


指令跟对应的类是一一对应的,然后再创建一个Class,用来读取配置文件,生成对应的实例,这个类起什么名字好呢?
它会生成很多不同类型的实例,好,暂且就叫它CommandFactory吧,然后给它一个静态方法,一个参数,返回值就是ICommand。

Code

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->public static ICommand getCommand(String comName)
{
    string comClsName = ConfigurationSettings.AppSettings[comName];
    if (comClsName != null)
    {
        Type type = Type.GetType(comClsName);
        _cmd = (ICommand)Activator.CreateInstance(type);
    }
    else
    {
        _cmd = new UnknownCommand();
    }
    return _cmd;
}


我都有点儿佩服我自己了,一个很好的解决办法,嗯,厉害!!!
Processor类也需要修改,以前是只能添加新的Command,但是如果用户第二次输入同样指令,就不应该添加,而应该是替换了。
到现在,最终的主方法已经是这样子的了:

Code

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->String command = "";
Processor p = new Processor();
while (true)
{
    command = Console.ReadLine();
    String cmdName = command.Split(SPACE.ToCharArray())[0];
    ICommand cmd = CommandFactory.getCommand(cmdName);

    if (command.IndexOf(SPACE) == -1)
    {
        cmd.setRequest(command);
    }
    else
    {
        cmd.setRequest(command.Substring(command.IndexOf(SPACE) + 1));
    }

    p.setCommand(cmdName, cmd);
    p.request();
}


比以前简洁了不少,而且每那么多的if else 和常量了,我的工作完成了。

高手看我沾沾自喜的样子,走过来,说:做完了,我看看。
看了一眼,又说:你比我当年不差啊,看过设计模式,是吧。
我老老实实的说:没看过,我就是觉得这么做应该就是比较好的做法,也不知道什么模式不模式的。
高手自言自语:是啊,你不受GOF设计模式的束缚,不强迫自己往他给定的UML类图上靠,而思想上却是相通的,小伙儿有前途啊。
听了高手这一顿白话,我信心倍增。
高手看我信心爆膨,说:别高兴的太早,刚才客户又有新的需求了,这回看你能不能瞎猫碰到死耗子身上,给我整出来。
我说:没问题。
不过,过了三分钟,我满面愁容的坐在电脑前,不知道如何下手。

http://www.kuqin.com/design-patterns/20080228/4002.html

评 论
还没有网友评论,欢迎您第一个评论!
博 主
进入imjacob的首页
博客名称:雅克的一府
日志总数:514
评论数量:901
访问次数:1859639
建立时间:2006-11-23 20:52
导 航
公 告
Locations of visitors to this page 本博客主要用于个人学习与资料收藏。当然大家应该读了之后也能学到不少东西。其中大多数资料都是来自网络,我转载时尽可能地表明文章出处与原作者姓名,但由于很多资料经多人转载,已不清楚原作者信息与出处,所以未表明相关…
评 论
链 接

ARM+LINUX 嵌入式博客
http://blog.chinaunix.net/u1/58780/index.html

嵌入式软件
http://blog.csdn.net/embeddedsoft

诚诚恳恳做人踏踏实实编程
http://blog.sina.com.cn/u/1244756857 

和我风格相似的一个blog
http://blogger.org.cn/blog/m…