Evil 域

当Evil遇上先知

导航

OOA&D方法之将变动部分抽取成类

Posted on 2007-08-26 23:10  Saar  阅读(396)  评论(4编辑  收藏  举报
一个好的软件总是能够满足用户的需求的,然而,用户的需求经常是变动的。用户需求的变动,对于程序设计人员来说永远是痛苦的。怎么让软件更能适应变化的需求?也许,在OOA&D里会有答案……
我们假设要建立一个软件搜索系统,UML类图如下:

此时,用户需求大致如下:
建立一个已有软件的软件列表,并且可以通过软件类型,搜索出全部相应的软件并输出之。
于是,根据用户的需求,我们建立了Software和SoftwareList两个类:
class Software
{
    
public Software(string id, string name, string version, string type)
    
{
        _id 
= id;
        _name 
= name;
        _version 
= version;
        _type 
= type;
    }


    
private string _id;

    
public string Id
    
{
        
get return _id; }
        
set { _id = value; }
    }

    
private string _name;

    
public string Name
    
{
        
get return _name; }
        
set { _name = value; }
    }

    
private string _version;

    
public string Version
    
{
        
get return _version; }
        
set { _version = value; }
    }

    
private string _type;

    
public string Type
    
{
        
get return _type; }
        
set { _type = value; }
    }


    
public override string ToString()
    
{
        
return string.Format("Software No.{0}: {1}, Ver.{2} belongs to {3}.\n"new string[] { _id, _name, _version, _type });
    }

}


class SoftwareList
{
    List
<Software> _softwares;
    
public SoftwareList()
    
{
        _softwares 
= new List<Software>();
    }


    
public void AddSoftware(string id, string name, string version, string type)
    
{
        Software sw 
= new Software(id, name, version, type);
        _softwares.Add(sw);
    }


    
public List<Software> SearchSoftware(Software softwareToSearch)
    
{
        List
<Software> result = new List<Software>();
        
foreach (Software currentSoftware in _softwares)
        
{
            
//只对比类型,过滤出符合用户要求的软件
            if (currentSoftware.Type.ToLower() == softwareToSearch.Type.ToLower())
            
{
                result.Add(currentSoftware);
            }

        }

        
return result;
    }

}
SoftwareList类提供了AddSoftware方法以供存储现有的软件,形成软件列表。SearchSoftware方法则提供了软件搜索机能。对应这样的类,我们来做一个测试类:TestV1
class TesterV1
{
    
static void Main(string[] args)
    
{
        SoftwareList softwares
=new SoftwareList();
        InitSoftList(softwares);
        
//The Searcher knows only type of the software he wanna
        Software softwareToSearch=new Software("","","","tools");
        List
<Software> result = softwares.SearchSoftware(softwareToSearch);
        
if (result.Count > 0)
        
{
            Console.WriteLine(
"There're the software you are interested in:\n");
            
foreach (Software soft in result)
            
{
                Console.WriteLine(soft.ToString());
            }


        }

        
else
        
{
            Console.WriteLine(
"Sorry, there's nothing you are interested in.");
        }

        Console.WriteLine(
"Press any key to continue");
        Console.ReadKey(
true);

    }


    
private static void InitSoftList(SoftwareList softwares)
    
{
        softwares.AddSoftware(
"1""WinRAR""3.70""Tools");
        softwares.AddSoftware(
"2""Windows""Vista""OS");
        softwares.AddSoftware(
"3""Thunder""5.6.9""Tools");
    }

}

运行它,会出现我们期望的结果,它似乎工作得很好。


但是,其实目前的设计中存在着很大的问题。问题在哪儿呢?突然有一天,痛苦的事情发生了:用户说,再给软件加一个“语言”属性,来标识该软件是什么语言的,例如中文啊、英文……很好,看来我们得给Software类加上一个语言属性。这时,问题来了,天哪,看来,我们还不得不修改我们的SoftwareList类,修改它的AddSoftware方法和SearchSoftware方法。也许,对于这样子一个小程序来说,这样的修改算不了什么,但是,在大型项目中,这样的设计导致的后果将是不堪设想的。一个类的修改影响到另外一个甚至多个类,那么,在一个拥有几十个类的项目里,这种修改的毁灭性的“效果”是可想而知的。
如何来避免这种灾难?其实,方法也很简单,只要把一个类的可变部分抽取成新的类即可。看看我们怎么修改我们的设计。
首先,第一步,把Software类里的可变部分抽取出来,形成一个新类:

 

public class SoftwareSpecific
{
    
public SoftwareSpecific(string type, string language)
    
{
        _type 
= type;
        _language 
= language;
    }


    
private string _type;

    
public string Type
    
{
        
get return _type; }
        
set { _type = value; }
    }

    
private string _language;

    
public string Language
    
{
        
get return _language; }
        
set { _language = value; }
    }


}

然后,修改原来的Software类,把变动属性从原来的类中去除,并添加特征类实例:

public class Software
{
    
public Software(string id, string name, string version, SoftwareSpecific sSpec)
    
{
        _id 
= id;
        _name 
= name;
        _version 
= version;
        
//_type = type;
        _sSpec = sSpec;
        
    }


    
private SoftwareSpecific _sSpec;
    
public SoftwareSpecific Specific
    
{
        
get return _sSpec; }
    }


    
private string _id;

    
public string Id
    
{
        
get return _id; }
        
set { _id = value; }
    }

    
private string _name;

    
public string Name
    
{
        
get return _name; }
        
set { _name = value; }
    }

    
private string _version;

    
public string Version
    
{
        
get return _version; }
        
set { _version = value; }
    }

    
//private string _type;

    
//public string Type
    
//{
    
//    get { return _type; }
    
//    set { _type = value; }
    
//}

    
public override string ToString()
    
{
        
return string.Format("Software No.{0}: {1}, Ver.{2} belongs to {3} and in language {4}.\n"new string[] { _id, _name, _version, _sSpec.Type, _sSpec.Language });
    }

}

最后,修改SoftwareList类:

public class SoftwareList
{
    List
<Software> _softwares;
    
public SoftwareList()
    
{
        _softwares 
= new List<Software>();
    }


    
public void AddSoftware(Software s)
    
{
        
//Software sw = new Software(id, name, version, type);
        _softwares.Add(s);
    }


    
public List<Software> SearchSoftware(SoftwareSpecific ssp)
    
{
        List
<Software> result = new List<Software>();
        
foreach (Software currentSoftware in _softwares)
        
{
            
//只对比类型,过滤出符合用户要求的软件
            if (currentSoftware.Specific.Type.ToLower() != ssp.Type.ToLower()) continue;
            
//添加对比语言,以找出符合用户要求的软件
            if (currentSoftware.Specific.Language.ToLower() != ssp.Language.ToLower()) continue;
            result.Add(currentSoftware);
        }

        
return result;
    }

}

哈哈,一个新的程序,添加了语言属性的新程序,又可以正常的运行了。下次要添加属性时,就不会再牵涉到除了SoftwareSpecific类以外的类了。呵呵,这就是把变动部分抽取成新类的方法。
等等,重新看一下代码,真的不需要改其它代码了?哦~,看看SoftwareList类的SearchSoftware方法吧,这里只进行了Type和Language的比较,如果下次添加了一个新的属性,岂不是又要添加一条语句?有没有好的办法来解决这个问题?呵呵,其实很简单,重载一下SoftwareSpecific类的Equals方法,就可以解决问题。我们来看代码片断:

public override bool Equals(object obj)
{
    SoftwareSpecific right 
= obj as SoftwareSpecific;
    
if (this._language.ToLower() != right._language.ToLower()) return false;
    
if (this._type.ToLower() != right._type.ToLower()) return false;
    
return true;
}
在重写了Equals方法之后,我们就可以修改我们的SoftwareList类中的查询方法了:
public List<Software> SearchSoftware(SoftwareSpecific ssp)
{
    List
<Software> result = new List<Software>();
    
foreach (Software currentSoftware in _softwares)
    
{
        
if (currentSoftware.Specific.Equals(ssp))
        
{
            result.Add(currentSoftware);
        }

    }

    
return result;
}


经过这么一翻折腾,终于使得我们在今后要添加更多属性时可以变得轻而易举了。
呵呵,初涉OOA&D,文章写得不好。欢迎有兴趣的朋友留言讨论啊。