面向对象分析设计学习与探索(二):好的应用程序设计(Well-designed apps rock)

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

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

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



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

    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;

     }

……

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

posted @ 2007-08-23 19:27  KiddLee  Views(4512)  Comments(21Edit  收藏  举报