Head.First.Object-Oriented.Design.and.Analysis《深入浅出面向对象的分析与设计》读书笔记(五)...

 

好的设计产生灵活的软件-上(good design = flexible software part 1)

------------没有什么是一成不变的nothing ever stays the same

 

 

 引言
  变化是不可避免的。无论现在你多么喜欢你的软件,明天还是可能会改变。你把软件开发的越难以改变,相应用户的需求变更就越困难。在本篇中我们将会视图改进一个已有的系统,看看多么小的一个改变能导致大问题。

 正文

      先来看一个已有系统的类关系图。就是一个guitar销售商店,可以查询guitar,添加guitar。

      客户说最近他想卖mandolins曼陀林(一种琵琶类乐器),想要我们在系统中实现这个功能,并且可以针对这种乐器进行搜索。

      我们需要在原有系统中支持曼陀林这种新的乐器。既然是乐器,肯定和guitar有共同的地方,我们可以抽象出来。加入一个乐器类,表示曼陀林和guitar的抽象。

 

 

     

      其实在上图中,我们会发现,乐器的细节类和guitar的细节类,以及mandolin的细节类都很相似,还应该有个抽象的乐器细节类。把共同的细节信息都移到抽象类中。

      就会变成下面的结果

 

 

 

 

 

      在UML图中

      如上图所示,第一条是关系,也就是通常我们说的类之间的关系,1:1,1:n,n:1,之类的。第二条就是继承。第三条是集成,还不太理解,在后面理解了我们再来讨论他。

      由上面最后的两张类图,我们可以写出下面的代码。

 

ExpandedBlockStart.gif 代码
<!--<br/ /> <br/ /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br/ /> http://www.CodeHighlighter.com/<br/ /> <br/ /> --> using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  BeautyCode.Common.ConApp.Head.First.OO.Design
{
    
public   abstract   class  InstrumentSpec
    {
        
private  Builder _builder;
        
private  InstrumentType _type;
        
private  Wood _backwood;
        
private  Wood _topwood;
        
public  InstrumentSpec(Builder builder, InstrumentType type, Wood backwood, Wood topwood)
        {
            _builder 
=  builder;
            _topwood 
=  topwood;
            _type 
=  type;
            _backwood 
=  backwood;
        }
        
public   virtual    bool  Matches(InstrumentSpec instance)
        {
            
if  (instance._builder  !=  _builder)
                
return   false ;
            
if  (instance._backwood  !=  _backwood)
                
return   false ;
            
if  (instance._topwood  !=  _topwood)
                
return   false ;
            
if  (instance._type  !=  _type)
                
return   false ;
            
return   true ;

        }
    }
    
public   abstract   class  Instrument
    {
        
private   string  _serialNumber;

        
public   string  SerialNumber
        {
            
get  {  return  _serialNumber; }
        }
        
private   double  _price;

        
public   double  Price
        {
            
get  {  return  _price; }
        }
        
private  InstrumentSpec _spec;

        
public  InstrumentSpec Spec
        {
            
get  {  return  _spec; }
        }
        
public  Instrument( string  serialNumber,  double  price, InstrumentSpec spec)
        {
            _serialNumber 
=  serialNumber;
            _price 
=  price;
            _spec 
=  spec;
        }

    }
}


using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  BeautyCode.Common.ConApp.Head.First.OO.Design
{
    
public   class  GuitarSpec : InstrumentSpec
    {
        
private   int  _numString;

        
public   int  NumString
        {
            
get  {  return  _numString; }

        }
        
public  GuitarSpec(Builder builder, InstrumentType type, Wood backwood, Wood topwood,  int  numString)
            : 
base (builder,
                type, backwood, topwood)
        {
            
this ._numString  =  numString;
        }
        
public   override   bool  Matches(InstrumentSpec instance)
        {
            
if  ( ! base .Matches(instance))
                
return   false ;
            
if  ( ! (instance  is  GuitarSpec))
                
return   false ;
            GuitarSpec spec 
=  instance  as  GuitarSpec;
            
if  (spec._numString  !=  _numString)
                
return   false ;
            
return   true ;
        }
    }

    
public   class  Guitar : Instrument
    {
        
public  Guitar( string  serialNumber,  double  price, GuitarSpec spec)
            : 
base (serialNumber, price, spec)
        {
        }
    }
}


using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  BeautyCode.Common.ConApp.Head.First.OO.Design
{
    
public   class  MandolinSpec : InstrumentSpec
    {
        
private  Style _style;

        
public  Style Style
        {
            
get  {  return  _style; }
        }
        
public  MandolinSpec(Builder builder, InstrumentType type, Wood backwood, Wood topwood, Style style)
            : 
base (builder,
                type, backwood, topwood)
        {
            
this ._style  =  style;
        }
        
public   override   bool  Matches(InstrumentSpec instance)
        {
            
if  ( ! base .Matches(instance))
                
return   false ;
            
if  ( ! (instance  is  GuitarSpec))
                
return   false ;
            MandolinSpec  spec 
=  instance  as  MandolinSpec ;
            
if  (spec._style   !=  _style )
                
return   false ;
            
return   true ;
        }
    }
    
public   class  Mandolin : Instrument
    {
        
public  Mandolin( string  serialNumber,  double  price, MandolinSpec spec)
            : 
base (serialNumber, price, spec)
        {
        }
    }
}


using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Text;

namespace  BeautyCode.Common.ConApp.Head.First.OO.Design
{
    
public   class  Inventory
    {
        
private  List < Instrument >  _inventory;
        
public  Inventory()
        {
            _inventory 
=   new  List < Instrument > ();
        }
        
public   void  AddInstrument( string  serialNumber, double  price,InstrumentSpec spec)
        {
            Instrument instance 
=   null ;
            
if  (spec  is  GuitarSpec)
            {
                instance 
=   new  Guitar(serialNumber, price,(GuitarSpec ) spec);
            }
            
else   if  (spec  is  MandolinSpec)
            {
                instance 
=   new  Mandolin(serialNumber, price, (MandolinSpec)spec);
            }
            _inventory.Add(instance);
        }
        
public  Instrument Get( string  serialNumber)
        {
            
return  _inventory.Find(i  =>  i.SerialNumber  ==  serialNumber);
        }
        
public  List < Instrument >  Search(MandolinSpec searchSpec)
        {
            List
< Instrument >  instances  =   _inventory  .FindAll(i  =>  i.Spec.Equals(searchSpec));
            
return  instances;
        }
    }
}

 

      是否这个就满足了要求了呢?现在看来是满足了用户的新要求。

  检查软件是否设计良好的一个方法就是加入变化,然后看看它是怎么适应变化的。

  现在如果要添加一个新的乐器类型,并且有对应的search方法。需要继承instrument基类,然后要修改inventory类,增加针对新乐器的search方法。当然了,增加新乐器,增加一个他的类和明细类没有问题,肯定是需要的。但是增加新乐器就修改一次inventory类,好像不太应该吧。

  让我们来引入一个新的概念:接口。

  接口是什么呢?接口是用来定义行为的,定义通用的行为。

  假设我们的系统有一个接口,很多类都通过实现这个接口来拥有常用的方法。编程的时候可以使用具体类,也可以使用接口。使用接口可以增加灵活性。你的代码工作在接口之上,就不用每次增加新类型就修改代码了。只要新类型实现接口就可以了。当然了,你也不要叫真,没有关系的,也不用非要实现接口。只是需要具有通用行为的类实现接口就行了。记住,接口是用来定义共同行为的。

  

  在你进行代码工作的任何时候,对类进行操作,你都有两个选择。你可以直接针对子类写代码,也就是FootballPlayer,也可以针对接口IAthlete写代码。当你面临这种抉择的时候,你应该记住:要针对接口编程,不要针对实现编程。

  为什么呢?就是为了给你的应用增加灵活性。在针对接口IAthlete编程之后,你的代码可以适用更多类型的athlete。而不用增加一种类型的athlete就为它们写一套重复的代码,给维护带来很大的麻烦。

  下面我们来解释一些概念

  1、封装

  在前面我们已经了解到,封装就是为了减少代码的复制。使用封装避免大量的copy-and-paste。封装还可以使你的类避免不必要的改变。

  在你的应用中,如果有的行为可能会发生变化,你就需要将这部分变化从很少变化的代码中移出来。换句话说,也就是封装变化,对可能经常变化的地方进行封装。

  2、变化

  你已经知道,在软件中唯一不变的就是变化。设计良好的软件很容易应对变化。

  最简单的办法就是保持软件的弹性,使得每个类只有一个改变它的原因。

  

     

 结论

      OO原则:

  •   封装变化
  •   针对接口编程,不针对实现编程
  •   每一个类应该只有一个改变他的原因

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值