惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

博客园 - KiddLee

数据库存储图像及使用Image控件显示 三层体系结构总结(六) Spring.Core IoC(一) Spring.Net 模块组成 Generating user instances in Sql Server is disabled. Use sp_configure 'user instances enabled' to generate user instances 面向对象分析设计学习与探索(六):好的设计=软件的灵活程度(good design=flexible software)继续 面向对象分析设计学习与探索(六):面向对象的灾难(OO Catastrophe) 面向对象分析设计学习与探索(六):好的设计=软件的灵活程度(good design=flexible software) 面向对象分析设计学习与探索(五):分析(Analysis) 面向对象分析设计学习与探索(四):需求变化(Requirements Change) 面向对象分析设计学习与探索(三):收集需求(Gathering Requirement) 面向对象分析设计学习与探索(一):开篇 类型构造器 装箱拆箱续 Session Cookie and Persistent Cookie 第一次执行方法 我犯错了 装箱拆箱 接口函数还可以声明为private
面向对象分析设计学习与探索(二):好的应用程序设计(Well-...
KiddLee · 2007-08-23 · via 博客园 - KiddLee

    如何能编写一个好的软件?这个问题我也一直在问我自己,每一次开发新的项目的时候,我都希望或争取这一次比上次做得好,至少我自己这么认为。虽然的确感觉每次都有一些成长,但是总是不能让人觉得满意。造成不满意的原因有很多,大多被我归咎到:时间紧任务重,所以劳动量大,能力有限,还有需求不明和需求不断的变化使我觉得继续做下去很乏味。我相信很多人跟我有同感。我也相信实际上这个问题很难回答,这有一个例子不错,不妨来看看。

情景:吉他店的老板决定要用一个查询系统代替原先的纸质纪录,以帮助他的客户更快的查询导向要的吉他。

根据用户所提供的需求,我们建立了两个类型:吉他和仓库。吉他类型中包含了吉他的各个属性;仓库类型中包含了吉他列表以及一些功能方法,如:添加吉他,获得吉他,查询吉他。

    现在就来实现这两个类型:

    public class Guitar

    {

        private String _serialNumber;

        private Double _price;

        private String _builder;

        private String _model;

        private String _type;

        private String _backWood;

        private String _topWood;

        public Guitar() { }

        public Guitar(String serialNumber, Double price,

            String builder, String model, String type, String backWood,

            String topWood)

        {

            this._serialNumber = serialNumber;

            this._price = price;

            this._builder = builder;

            this._model = model;

            this._type = type;

            this._backWood = backWood;

            this._topWood = topWood;

        }

        public String SerialNumber

        {

            get { return _serialNumber; }

            set { _serialNumber = value; }

        }

        public Double Price

        {

            get { return _price; }

            set { _price = value; }

        }

        public String Builder

        {

            get { return _builder; }

            set { _builder = value; }

        }

        public String Model

        {

            get { return _model; }

            set { _model = value; }

        }

        public String Type

        {

            get { return _type; }

            set { _type = value; }

        }

        public String BackWood

        {

            get { return _backWood; }

            set { _backWood = value; }

        }

        public String TopWood

        {

            get { return _topWood; }

            set { _topWood = value; }

        }

    }

    public class Inventory

    {

        List<Guitar> guitars;

        public Inventory()

        {

            guitars = new List<Guitar>();

        }

        public Boolean addGuitar(String serialNumber, Double price,

            String builder, String model, String type, String backWood,

            String topWood)

        {

            guitars.Add(new Guitar(serialNumber, price, builder, model, type, backWood, topWood));

            return true;

        }

        public Guitar getGuitar(String serialNumber)

        {

            Guitar reseachGuitar = null;

            foreach (Guitar guitar in guitars)

            {

                if (guitar.SerialNumber == serialNumber)

                {

                    reseachGuitar = guitar;

                    break;

                }

            }

            return reseachGuitar;

        }

        public List<Guitar> searchGuitar(Guitar searchGuitar)

        {

            List<Guitar> list = new List<Guitar>();

            foreach (Guitar guitar in guitars)

            {

                if (guitar.SerialNumber != searchGuitar.SerialNumber) { continue; }

                if (guitar.BackWood != searchGuitar.BackWood) { continue; }

                if (guitar.Builder != searchGuitar.Builder) { continue; }

                if (guitar.Model != searchGuitar.Model) { continue; }

                if (guitar.Price != searchGuitar.Price) { continue; }

                if (guitar.TopWood != searchGuitar.TopWood) { continue; }

                if (guitar.Type != searchGuitar.Type) { continue; }

                list.Add(guitar);

            }

            return list;

 }

顾客只需要将其想要吉他的信息输入,系统调用searchGuitar函数就可以查询出相应的吉他列表。下面我们加入测试代码:

    public class Test

    {

        static Inventory inventory;

        static void Main(string[] args)

        {

            InventoryInit();

            Guitar searchguitar = new Guitar("", 0, "builder1", "model1", "type1", "backwood1", "topwood1");

            List<Guitar> guitarlist = inventory.searchGuitar(searchguitar);

            if (guitarlist.Count != 0)

            {

                foreach (Guitar guitar in guitarlist)

                {

                    Console.WriteLine("Find the Guitar's SerialNumber is" + guitar.SerialNumber);

                }

            }

            else

            {

                Console.WriteLine("Can't find the Guitar");

            }

            Console.Read();

        }

        public static void InventoryInit()

        {

            inventory = new Inventory();

            inventory.guitars = new List<Guitar>();

            inventory.guitars.Add(new Guitar("0001", 123, "builder1", "model1", "type1", "backwood1", "topwood1"));

            inventory.guitars.Add(new Guitar("0002", 123, "builder2", "model2", "type2", "backwood2", "topwood2"));

        }

}

查询结果如下

    

但是结果往往并不像我们想象得那么顺利。比如说用户在查找时输入如下时查询结果就不尽人意了

("0001", 123, "builder1", "Model1", "type1", "backwood1", "topwood1")

 


第一件事我们需要改变什么?如何写一个好的软件?

                首先,好的软件必须满足客户需求。软件必须做客户想让它做的事情。

                第二,好的软件是好的设计,好的编码,简单的维护,重用和扩展

换句话说,好的软件需要三步:

回来再看我的例子,要让刚才的程序变为一个好的软件,第一步是要确定我的软件做客户想让它做的事情。但是现在由于客户输入的信息在大小写上和存储的信息有偏差,造成的结果无法查找出来。不过要注意:在解决问题时不要带入新的问题。现在所要做的是满足客户需求。既然查询的时候因为大小写有偏差,那就把客户的输入信息和要比对的信息都换成小写。修改Inventory类型中的searchGuitar

public List<Guitar> searchGuitar(Guitar searchGuitar)

        {

            List<Guitar> list = new List<Guitar>();

            foreach (Guitar guitar in guitars)

            {

                if (guitar.BackWood.ToLower() != searchGuitar.BackWood.ToLower()) { continue; }

                if (guitar.Builder.ToLower() != searchGuitar.Builder.ToLower()) { continue; }

                if (guitar.Model.ToLower() != searchGuitar.Model.ToLower()) { continue; }

                if (guitar.TopWood.ToLower() != searchGuitar.TopWood.ToLower()) { continue; }

                if (guitar.Type.ToLower() != searchGuitar.Type.ToLower()) { continue; }

                list.Add(guitar);

            }

            return list;

        }


               
现在这个问题解决了,程序也就基本满足了客户的需要。但是这第一步就结束了吗?现实生活中当客户开始使用编写好的工具的时候,他的想象力的大门也就慢慢打开了。我们能做的只能是尽力去满足他。当然我认为是尽力,而不是必须。因为有结项时间在那,首先要保证的是整体功能。记住,写一个好的软件的第一步是要确定你的软件做的是客户想要做的。

当我们根据客户的新要求编写好软件后,实际上只完成了第一步。但是在做项目期间,常常是完成了这一步就不做了。所以到最后随着新的模块或者功能的增加,项目变得异常混乱。实际上写完代码并实现功能只是第一步,很多公司往往不太重视对于代码的重构,甚至是不希望做代码重构,因为老板眼里,代码重构只会加重成本。不过我觉得重构可以减少今后维护或者是继续开发的风险。现在开始重新审视写好的代码,改进其中不合理的地方,或者说不符合面向对象的地方。

在上面的代码例子中,有没有觉得那个查询方法有些奇怪。实际上我们应该查的是某一类型的吉他,而且不需要填入吉他的编号和价格,或者说searchGuitar方法中应该按照一个吉他的规格去查询。当然,每个吉他也对应的属于某个规格。添加吉他规格类型GuitarSpec,并修改Guitar


   
public
class GuitarSpec

    {

        private String _builder;

        private String _model;

        private String _type;

        private String _backWood;

        private String _topWood;

        public GuitarSpec(String builder,

            String model, String type,

            String backWood, String topWood)

        {

            this._builder = builder;

            this._model = model;

            this._type = type;

            this._backWood = backWood;

            this._topWood = topWood;

        }

        public String Builder

        {

            get { return _builder; }

            set { _builder = value; }

        }

        public String Model

        {

            get { return _model; }

            set { _model = value; }

        }

        public String Type

        {

            get { return _type; }

            set { _type = value; }

        }

        public String BackWood

        {

            get { return _backWood; }

            set { _backWood = value; }

        }

        public String TopWood

        {

            get { return _topWood; }

            set { _topWood = value; }

        }

    }

    public class Guitar

    {

        private String _serialNumber;

        private Double _price;

        private GuitarSpec _spec;

        public Guitar() { }

        public Guitar(String serialNumber, Double price,

            String builder, String model, String type, String backWood,

            String topWood)

        {

            this._serialNumber = serialNumber;

            this._price = price;

            this._spec = new GuitarSpec(builder, model, type, backWood, topWood);           

        }

        public String SerialNumber

        {

            get { return _serialNumber; }

            set { _serialNumber = value; }

        }

        public Double Price

        {

            get { return _price; }

            set { _price = value; }

        }

        public GuitarSpec Spec

        {

            get { return _spec; }

            set { _spec = value; }

        }

     }

       这样对于吉他的查询方法也就要作相应的调整,调整如下:

public List<Guitar> searchGuitar(GuitarSpec spec)

        {

            List<Guitar> list = new List<Guitar>();

            foreach (Guitar guitar in guitars)

            {

                if (guitar.Spec.BackWood.ToLower() != spec.BackWood.ToLower()) { continue; }

                if (guitar.Spec.Builder.ToLower() != spec.Builder.ToLower()) { continue; }

                if (guitar.Spec.Model.ToLower() != spec.Model.ToLower()) { continue; }

                if (guitar.Spec.TopWood.ToLower() != spec.TopWood.ToLower()) { continue; }

                if (guitar.Spec.Type.ToLower() != spec.Type.ToLower()) { continue; }

                list.Add(guitar);

            }

            return list;

        }

       最后是测试方法中的查找部分要进行调整:

     GuitarSpec spec = new GuitarSpec("builder1", "Model1", "type1", "backwood1", "topwood1");

            List<Guitar> guitarlist = inventory.searchGuitar(spec);

       这样相对于第一次编写的程序来说,这个更好一些。程序比第一次更加符合面向对象的原则。当然还可以有很多改进,在这里就不一一列举了。

       到现在,第二步可以告一段落。接下来,让我们考虑一些重用和如何才能使软件的修改更加容易。这样才是一个好的设计,并且能使软件变成一个可重用、有可扩展价值的软件。

       比如说现在吉他店的老板需要添加新的需求,如:为吉他多加一个琴弦数量的属性,那我们就需要如何修改呢?我们的这个程序设计是否比较好呢?

       应该在GuitarSpec类型中加入一个属性,当然也要修改Inventory类型中的查询方法和Guitar类型的构造函数。

       首先修改GuitarSpec类型

    public class GuitarSpec

    {

        ……

        private int _numStrings;

        public GuitarSpec(String builder,

            String model, String type,

            String backWood, String topWood, int numStrings)

        {

            this._builder = builder;

            this._model = model;

            this._type = type;

            this._backWood = backWood;

            this._topWood = topWood;

            this._numStrings = numStrings;

        }

        ……

        public int NumStrings

        {

            get { return _numStrings; }

            set { _numStrings = value; }

        }

}

然后再修改Guitar的构造函数:

public Guitar(String serialNumber, Double price,

            String builder, String model, String type, String backWood,

            String topWood, int numStrings)

        {

            this._serialNumber = serialNumber;

            this._price = price;

            this._spec = new GuitarSpec(builder, model, type, backWood, topWood, numStrings);           

     }

最后,修改Inventory类型中的查询方法以及因为Guitar类型构造函数变化造成的必要的修改

public List<Guitar> searchGuitar(GuitarSpec spec)

        {

            List<Guitar> list = new List<Guitar>();

            foreach (Guitar guitar in guitars)

            {

                if (guitar.Spec.BackWood.ToLower() != spec.BackWood.ToLower()) { continue; }

                if (guitar.Spec.Builder.ToLower() != spec.Builder.ToLower()) { continue; }

                if (guitar.Spec.Model.ToLower() != spec.Model.ToLower()) { continue; }

                if (guitar.Spec.TopWood.ToLower() != spec.TopWood.ToLower()) { continue; }

                if (guitar.Spec.Type.ToLower() != spec.Type.ToLower()) { continue; }

                if (guitar.Spec.NumStrings != spec.NumStrings) { continue; }

                list.Add(guitar);

            }

            return list;

     }

……

   这个例子只是简单的描述了一个软件开发的过程。不过由此可见,想要做一个好的软件也不是无规则可循,关键是记住上面的步骤,并且,做好每一步。