欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

一個(gè)逐步“優(yōu)化”的范例程序

系統(tǒng) 1955 0
reference URL:
http://www.tracefact.net/Software-Design/A-Sample-Design.aspx

本文是《Object-Oriented Analysis and Design》一書(shū)第一章和第五章的讀書(shū)筆記。我對(duì)書(shū)中提供的一個(gè)范例程序進(jìn)行了總結(jié)和整理,通過(guò)逐步優(yōu)化這個(gè)樂(lè)器管理的范例程序,分析了進(jìn)行程序設(shè)計(jì)時(shí)需要注意到的一些問(wèn)題。

1.簡(jiǎn)單直接的實(shí)現(xiàn)

這個(gè)程序起初的需求很簡(jiǎn)單: 我們需要?jiǎng)?chuàng)建一個(gè)吉他管理程序,它能夠保存所有的吉他信息,并且可以通過(guò)輸入吉他的參數(shù)來(lái)進(jìn)行查詢(xún),返回查詢(xún)結(jié)果。 我們知道一個(gè)優(yōu)良的軟件應(yīng)該從兩個(gè)角度去衡量:

  • 從用戶(hù)的角度,軟件應(yīng)該是符合用戶(hù)期望的,也就是滿(mǎn)足了用戶(hù)的需求,可以完成用戶(hù)期望它完成的工作。
  • 從開(kāi)發(fā)者的角度,軟件應(yīng)該是易于維護(hù)的、可擴(kuò)展的,以及可重用的。

這兩個(gè)方面應(yīng)該是遞進(jìn)的,也就是說(shuō)軟件首先要能滿(mǎn)足用戶(hù)的需求。所以我們先看如何完成用戶(hù)的需求,我們定義一個(gè)類(lèi)Guitar,它代表了吉他;以及一個(gè)Inventory類(lèi),它用于維護(hù)現(xiàn)有吉他的信息,可以進(jìn)行添加、查找等操作:

一個(gè)逐步“優(yōu)化”的范例程序

然后我們來(lái)看下實(shí)現(xiàn):

// 吉他類(lèi)
public class Guitar {
??? private string serialNumber;?? // 序列號(hào)
??? private string builder;??????? // 廠(chǎng)商
??? private string model;????????? // 型號(hào)
??? private string type;?????????? // 類(lèi)型
??? private string backWood;?????? // 后部材質(zhì)
??? private string topWood;??????? // 前面材質(zhì)
??? private double price;????????? // 價(jià)格

??? public Guitar( string serialNumber, string builder, string model, string type, string backWood, string topWood, double price) {
??????? this .backWood = backWood;
??????? this .builder = builder;
??????? this .model = model;
??????? this .price = price;
??????? this .serialNumber = serialNumber;
??????? this .topWood = topWood;
??????? this .type = type;
??? }
???
??? // 公有屬性省略
}

public class Inventory {
??? // 維護(hù)現(xiàn)有的所有吉他
??? private List<Guitar> guitarList;
??? public Inventory() {
??????? guitarList = new List <Guitar>();
??? }

??? // 向列表中添加 吉他
??? public void AddGuitar( string serialNumber, string builder, string model, string type, string backWood, string topWood, double price) {
??????? Guitar guitar = new Guitar (serialNumber, builder, model, type,backWood, topWood, price);

??????? guitarList.Add(guitar);
??? }

??? // 搜索吉他列表,尋找滿(mǎn)足searchGuitar參數(shù)的吉他
??? // 如果searchGuitar的參數(shù)為null或者"",則忽略此參數(shù)
??? public Guitar Search(Guitar searchGuitar) {
??????? List<Guitar>. Enumerator it =? guitarList.GetEnumerator();
??????? // 價(jià)格Price和序列號(hào)SerialNumber不參與查詢(xún)
??????? Guitar result = null ;
??????? while (it.MoveNext()) {
??????????? Guitar guitar = it.Current;
??????????? string builder = searchGuitar.Builder;
??????????? if (! String .IsNullOrEmpty(builder) &&
??????????????? !builder.Equals(guitar.Builder))
??????????????? continue ;

??????????? string model = searchGuitar.Model;
??????????? if (! String .IsNullOrEmpty(model) &&
??????????????? !model.Equals(guitar.Model))
??????????????? continue ;

??????????? string type = searchGuitar.Type;
??????????? if (! String .IsNullOrEmpty(type) &&
??????????????? !type.Equals(guitar.Type))
??????????????? continue ;

??????????? string backWood = searchGuitar.BackWood;
??????????? if (! String .IsNullOrEmpty(backWood) &&
??????????????? !backWood.Equals(guitar.BackWood))
??????????????? continue ;

??????????? string topWood = searchGuitar.TopWood;
??????????? if (! String .IsNullOrEmpty(topWood) &&
??????????????? !topWood.Equals(guitar.TopWood))
??????????????? continue ;

??????????? result = guitar;?? // 找到第一個(gè)匹配結(jié)果就返回
??????????? return result;
??????? }

??????? return result;
??? }
}

接下來(lái)我們向Inventory中添加一些Guitar,然后來(lái)測(cè)試下查找功能:

class Program {
??? static void Main ( string [] args) {

??????? Inventory inventory = new Inventory ();
??????? initializeInventory(inventory);

??????? // 想要查找的Guitar
??????? Guitar wanted = new Guitar ( "", "fender", "Stratocastor", "electric", "Alder", "Alder" , 0);

??????? // 返回符合條件的結(jié)果
??????? Guitar guitar = inventory.Search(wanted);

??????? if (guitar != null ) { // 找到符合條件的結(jié)果
??????????? Console .WriteLine( "You might like this {0} {1} {2} guitar:\n {3} back and sides,\n {4} top.\n You can have it for only ${5} !" , guitar.Builder, guitar.Model, guitar.Type, guitar.BackWood, guitar.TopWood, guitar.Price);
??????? } else {
??????????? Console .WriteLine( "Sorry, nothing found." );
??????? }
??? }

??? private static void initializeInventory(Inventory item) {
??????? item.AddGuitar( "V95693", "Fender", "Stratocastor", "electric", "Alder", "Alder" , 1499.95D);
?????? item.AddGuitar( "B95315", "Gibson", "SpecialKind", "electric", "Maple", "Cedar" , 2134.30D);
??????? item.AddGuitar( "V95694", "Fender", "Stratocastor", "electric", "Alder", "Alder" , 1599.95D);
??? }
}

結(jié)果我們發(fā)現(xiàn)并未返回搜索結(jié)果,但是我們看下InitializeInventory()方法,確實(shí)存在一個(gè)Guitar,它的屬性完全符合要查找的Guitar實(shí)例wanted。為什么查詢(xún)卻找不到呢?仔細(xì)查看一下,我們發(fā)現(xiàn)添加到Inventory中的Guitar的制造商Builder是"Fender",而輸入的searchGuitar的Builder屬性為"fender"。我們知道,在C#中字符串的大小寫(xiě)是敏感的,即是說(shuō) "a"=="A"返回的是false,所以"Fender"不等于"fender"。所以我們遇到的問(wèn)題是:在不要求嚴(yán)格匹配大小寫(xiě)的情況下,對(duì)于字符串的比較,我們應(yīng)該先全部轉(zhuǎn)換為大寫(xiě)或者小寫(xiě),然后再進(jìn)行比較。但是這樣做就沒(méi)有問(wèn)題了么?我們看一下Guitar類(lèi)的定義,除了price為double類(lèi)型以外,其余均為string。而某些屬性,比如說(shuō)吉他的發(fā)聲類(lèi)型type,只有兩種可能,一種是傳統(tǒng)的、通過(guò)震動(dòng)發(fā)聲的(Acoustic),一種是電子發(fā)聲的(Electric);對(duì)于制造商Builder,可能只有有限的幾個(gè)廠(chǎng)家。但使用string類(lèi)型時(shí),我們無(wú)法對(duì)于這些屬性的取值進(jìn)行限制,此時(shí),我們應(yīng)該考慮: 如果對(duì)象的屬性是由有限個(gè)項(xiàng)目構(gòu)成的集合,我們最好定義一個(gè)枚舉,并設(shè)置對(duì)象的屬性為這個(gè)枚舉類(lèi)型。

所以對(duì)于上面程序可以進(jìn)行的第一個(gè)改進(jìn),就是定義枚舉,并將部分屬性的值,由string改為枚舉類(lèi)型:

// 發(fā)生類(lèi)型
public enum SoundType {
??? Acoustic, Electric
}
// 制造商
public enum Builder {
??? Fender, Martin, Gibson, Collings, Olson
}
// 木料
public enum Wood {
??? IndianRoseWood, BrazilianRoseWood, Mahogany, Maple, Cocobolo, Cedar, Alder, Sitka
}

同時(shí)修改Guitar類(lèi)和Inventory類(lèi),讓它們使用這些枚舉作為字段類(lèi)型:

public class Guitar {
??? private string serialNumber;?? // 序列號(hào)
??? private Builder builder;?????? // 廠(chǎng)商
??? private string model;????????? // 型號(hào)
??? private SoundType type;??????? // 類(lèi)型
??? private Wood backWood;???????? // 后部材質(zhì)
??? private Wood topWood;????????? // 前面材質(zhì)
??? private double price;????????? // 價(jià)格
???
??? // 構(gòu)造函數(shù)、屬性做相應(yīng)修改,此處略
}

此時(shí),我們發(fā)現(xiàn)上面例子Inventory中符合搜索條件的有兩項(xiàng),而Search()方法只能返回查詢(xún)到的第一個(gè)結(jié)果,所以第二處改進(jìn)就是對(duì)Inventory的Search()方法進(jìn)行修改,讓它返回一個(gè)查詢(xún)結(jié)果列表:

public class Inventory {??
??? private List<Guitar> guitarList; // 維護(hù)現(xiàn)有的所有吉他

??? public Inventory() {
??????? guitarList = new List <Guitar>();
??? }

??? // AddGuitar()方法略...

??? // 搜索吉他列表,尋找滿(mǎn)足searchGuitar參數(shù)的吉他
??? public List<Guitar> Search(Guitar searchGuitar) {
??????? List<Guitar>. Enumerator it = guitarList.GetEnumerator();

??????? List<Guitar> list = new List <Guitar>();??? // 保存滿(mǎn)足搜索條件的吉他
??????? while (it.MoveNext()) {
??????????? Guitar guitar = it.Current;

??????????? if (guitar.Builder!=searchGuitar.Builder)
??????????????? continue ;
??????????? string model = searchGuitar.Model.ToLower();
??????????? if (! String .IsNullOrEmpty(model) &&
??????????????? !model.Equals(guitar.Model.ToLower()))
??????????????? continue ;
??????????? if (guitar.Type != searchGuitar.Type)
??????????????? continue ;?????????????????????????
??????????? if (guitar.BackWood != searchGuitar.BackWood)
??????????????? continue ;
??????????? if (guitar.TopWood != searchGuitar.TopWood)
??????????????? continue ;

??? ??????? list.Add(guitar);? // 添加到列表中
??????? }

??????? return list;??? // 返回結(jié)果
??? }
}

然后我們進(jìn)行一下測(cè)試,可以看到它返回了兩個(gè)結(jié)果。

static void Main ( string [] args) {?????????
??? Inventory inventory = new Inventory ();
??? initializeInventory(inventory);

??? // 想要查找的Guitar
??? Guitar wanted = new Guitar ( "", Builder.Fender, "Stratocastor" , SoundType.Electric, Wood.Alder, Wood.Alder, 0);
???????????????
??? // 返回符合條件的結(jié)果
??? List<Guitar> list = inventory.Search(wanted);

??? if (list.Count > 0) {
??????? foreach (Guitar guitar in list) {
??????????? Console .WriteLine( "You might like this {0} {1} {2} guitar:\n {3} back and sides,\n {4} top.\n You can have it for only ${5} !" , guitar.Builder, guitar.Model, guitar.Type, guitar.BackWood, guitar.TopWood, guitar.Price);
??????? }
??? } else {
??????? Console .WriteLine( "Sorry, not found." );
??? }
}

一個(gè)逐步“優(yōu)化”的范例程序

這里仍然需要注意一個(gè)問(wèn)題:上面我們將Guitar的字段類(lèi)型由string改為了枚舉,雖然我們限制了輸入,字段只能接受有限的數(shù)值,但是我們?cè)谡{(diào)用Search()方法時(shí),必須明確的指定一個(gè)枚舉值。而有時(shí)候,我們并不希望指明數(shù)值(我們希望忽略此查詢(xún)條件),比如說(shuō),我們不希望限制吉他的木料(任何木料的吉他都滿(mǎn)足查詢(xún)條件),在使用string類(lèi)型時(shí),我們只需要傳遞null或者空字符串("")進(jìn)去就可以了,但使用枚舉后卻必須指定一個(gè)數(shù)值。此時(shí),可以向枚舉中添加一個(gè)字段,NotSet,這個(gè)值相當(dāng)于string為null或空字符串("")時(shí)的情況。然后將Search()方法中的判斷語(yǔ)句進(jìn)行一下修改就可以了:

if (searchGuitar.TopWood != Wood.NotSet &&
??? guitar.TopWood != searchGuitar.TopWood)
??? continue ;

2.屬性分離和解耦

屬性分離

我們?cè)賹?duì)上面的程序稍微進(jìn)行一下分析,發(fā)現(xiàn)對(duì)于Guitar來(lái)說(shuō),SerialNumber和Price屬性是一定會(huì)有的,而其他的屬性以后可能會(huì)添加,比如說(shuō)我們可能會(huì)再添加一個(gè)NumStrings屬性,代表吉他有多少根玄;也可能會(huì)刪除某個(gè)屬性,比如我們可能以后會(huì)覺(jué)得model屬性多余,然后把它刪除掉。除此以外,我們發(fā)現(xiàn)Inventory類(lèi)的Search()方法只需要Guitar的部分屬性,而我們傳遞了整個(gè)Guitar進(jìn)去。

此時(shí), 我們可以將不變的部分(SerialNumber和Price)仍保留在Guitar類(lèi)中,將可能會(huì)變化的部分(Guitar類(lèi)的其他屬性),封裝為另一個(gè)類(lèi)型,我們稱(chēng)為GuitarSpec,并在Guitar中保存一個(gè)GuitarSpec類(lèi)型實(shí)例:

一個(gè)逐步“優(yōu)化”的范例程序

public class GuitarSpec {
??? private Builder builder;?????? // 廠(chǎng)商
??? private string model;????????? // 型號(hào)
??? private SoundType type;??????? // 類(lèi)型
??? private Wood backWood;???????? // 后部材質(zhì)
??? private Wood topWood;????????? // 前面材質(zhì)

??? public GuitarSpec(Builder builder, string model, SoundType type, Wood backWood, Wood topWood) {
??????? this .backWood = backWood;
??????? this .builder = builder;
??????? this .model = model;
??????? this .topWood = topWood;
??????? this .type = type;
??? }
??? // 屬性略
}

解耦

由于GuitarSpec成為了一個(gè)獨(dú)立的對(duì)象,所以,我們的Guitar類(lèi)型只需要保存一個(gè)GuitarSpec對(duì)象就可以了:

public class Guitar {
??? private string serialNumber;?? // 序列號(hào)
??? private double price;????????? // 價(jià)格
??? private GuitarSpec spec;?????? // 吉他屬性集
??? // 略...
}

此處有一個(gè)地方值得注意, Guitar的構(gòu)造函數(shù)通常會(huì)有下面兩種寫(xiě)法:

public Guitar( string serialNumber, Builder builder, string model, SoundType type, Wood backWood, Wood topWood, double price) {
??? this .price = price;
??? this .serialNumber = serialNumber;
??? this .spec = new GuitarSpec (builder, model, type, backWood, topWood);
}

public Guitar( string serialNumber, double price, GuitarSpec spec) {
??? this .price = price;
??? this .serialNumber = serialNumber;
??? this .spec = spec;
}

采用第一種寫(xiě)法時(shí),我們?cè)贕uitar類(lèi)的構(gòu)造函數(shù)中創(chuàng)建GuitarSpec類(lèi)型實(shí)例,第二種在Guitar類(lèi)外部先行創(chuàng)建好,然后再傳入。那么采用那種方式好呢?我們回想一下,創(chuàng)建GuitarSpec的目的就是為了將易變化的部分從Guitar類(lèi)中隔離出去,而采用第一種方式時(shí),無(wú)異于再次將變化重新引入Guitar類(lèi),因?yàn)楫?dāng)我們向GuitarSpec類(lèi)添加或刪除屬性時(shí),必須同時(shí)修改Guitar類(lèi)的構(gòu)造函數(shù)!所以,這里我們采用第二種方式的構(gòu)造函數(shù)。

類(lèi)似的我們修改Inventory類(lèi)的AddGuitar()方法和Search()方法:

// 向列表中添加 吉他
public void AddGuitar( string serialNumber, double price, GuitarSpec spec) {
??? Guitar guitar = new Guitar (serialNumber, price, spec);
??? guitarList.Add(guitar);
}

// 搜索吉他列表,尋找滿(mǎn)足searchSpec參數(shù)的吉他
public List<Guitar> Search(GuitarSpec searchSpec) {
??? List<Guitar>. Enumerator it = guitarList.GetEnumerator();

??? List<Guitar> list = new List <Guitar>(); // 保存滿(mǎn)足搜索條件的吉他
??? while (it.MoveNext()) {
??????? GuitarSpec guitarSpec = it.Current.Spec;

??????? if (guitarSpec.Builder != searchSpec.Builder)
??????????? continue ;
??????? string model = searchSpec.Model.ToLower();
??????? if (! String .IsNullOrEmpty(model) &&
??????????? !model.Equals(guitarSpec.Model.ToLower()))
??????????? continue ;
??????? if (guitarSpec.Type != searchSpec.Type)
??????????? continue ;
??????? if (guitarSpec.BackWood != searchSpec.BackWood)
??????????? continue ;
??????? if (guitarSpec.TopWood != searchSpec.TopWood)
??????????? continue ;

??????? list.Add(it.Current);? // 添加到列表中
??? }

??? return list;??? // 返回結(jié)果
}

現(xiàn)在看上去程序已經(jīng)完善的差不多了,我們上面做得這些都是為了能夠在Guitar的屬性變化的時(shí)候,盡可能的少做修改。檢驗(yàn)程序是否經(jīng)得起變化的一個(gè)方法就是我們現(xiàn)在假設(shè)刪除一個(gè)屬性model,看看需要改變哪些地方:我們得出Guitar類(lèi)是不需要進(jìn)行修改的,GuitarSpec類(lèi)需要?jiǎng)h除model屬性, 然而,我們發(fā)現(xiàn)Inventory類(lèi)也需要進(jìn)行修改,因?yàn)樗腟earch方法依賴(lài)于guitarSpec類(lèi)的Model屬性,因?yàn)橐獙?duì)它進(jìn)行判斷。 此時(shí),我們說(shuō)Inventory類(lèi)與GuitarSpec類(lèi)是耦合在一起的。那么如何才能使得修改GuitarSpec類(lèi)不需要改動(dòng)Inventory類(lèi)呢?我們可以將對(duì)GuitarSpec進(jìn)行判等的操作,委托給GuitarSpec類(lèi)型本身來(lái)完成,我們讓GuitarSpec類(lèi)實(shí)現(xiàn)IEquatable<T>接口:

public class GuitarSpec :IEquatable<GuitarSpec> {
??? // 其余略...????????
??? public bool Equals(GuitarSpec other) {
??????? if (builder != other.Builder)
??????????? return false ;
??????? string model = other.Model.ToLower();
??????? if (! String .IsNullOrEmpty(model) &&
??????????? ! model.Equals( this .model.ToLower()))
??????????? return false ;
??????? if (type != other.Type)
??????????? return false ;
??????? if (backWood != other.BackWood)
??????????? return false ;
??????? if (topWood != other.TopWood)
??????????? return false ;

??????? return true ;
??? }
}

現(xiàn)在判斷兩個(gè)GuitarSpec是否相等的邏輯轉(zhuǎn)移到了GuitarSpec類(lèi)型本身,我們?cè)俅涡薷腎nventory的Search()方法,讓它將對(duì)GuitarSpec的判等操作委托出去。

// 搜索吉他列表,尋找滿(mǎn)足searchSpec參數(shù)的吉他
public List<Guitar> Search(GuitarSpec searchSpec) {
??? List<Guitar>. Enumerator it = guitarList.GetEnumerator();

??? List<Guitar> list = new List <Guitar>(); // 保存滿(mǎn)足搜索條件的吉他
??? while (it.MoveNext()) {
??????? GuitarSpec guitarSpec = it.Current.Spec;

??????? if (guitarSpec.Equals(searchSpec)) // 進(jìn)行兩個(gè)對(duì)象的判等
??????????? list.Add(it.Current);? // 將結(jié)果添加到列表中
??? }

??? return list;??? // 返回結(jié)果
}

經(jīng)過(guò)現(xiàn)在的修改之后,不僅Search()方法的實(shí)現(xiàn)變得更為簡(jiǎn)單,各個(gè)類(lèi)的職責(zé)也更加清晰,我們修改GuitarSpec類(lèi)型也不會(huì)影響到Inventory類(lèi)和Guitar類(lèi)。

3.抽象和繼承

接下來(lái)我們來(lái)對(duì)上面的程序進(jìn)行一下擴(kuò)展,假如我們的程序不僅需要對(duì)吉他(Guitar)進(jìn)行管理和維護(hù),還需要對(duì)曼陀林(Mandolin,一種琵琶樂(lè)器)進(jìn)行管理,它的屬性與吉他是類(lèi)似的,但是多了一個(gè)Style屬性,有"A"和"F"兩種取值;同時(shí)我們?yōu)榧偌尤胍粋€(gè)NumStrings屬性,代表玹的數(shù)量,那么該如何改進(jìn)程序呢?

首先我們創(chuàng)建一個(gè)Style枚舉,它只包含A、F兩個(gè)枚舉值。接下來(lái),我們可以將Guitar類(lèi)和Mandolin的公共部分抽象出來(lái),建立一個(gè)Instrument基類(lèi),這個(gè)Instrument基類(lèi)包含Guitar和Mandolin公有的部分,然后讓Guitar和Mandolin繼承自Instrument。 因?yàn)槲覀儗?shí)際上并不需要?jiǎng)?chuàng)建一個(gè)Instrument的實(shí)例,所以我們將它聲明為抽象的。 類(lèi)似的,我們將GuitarSpec也抽象為InstrumentSpec,并且再為Mandolin創(chuàng)建一個(gè)MandolinSpec類(lèi),讓GuitarSpec和MandolinSpec繼承自InstrumentSpec:

// Instrument樂(lè)器基類(lèi)
public abstract class Instrument {
??? private string serialNumber;?? // 序列號(hào)
??? private double price;????????? // 價(jià)格
??? private InstrumentSpec spec;?? // 樂(lè)器屬性集

??? // 構(gòu)造函數(shù)和屬性略
}
// 吉他類(lèi)
public class Guitar : Instrument {
?? public Guitar( string serialNumber, double price, GuitarSpec spec): base (serialNumber, price, spec) {
??? }
}
// 曼陀林類(lèi)
public class Mandolin : Instrument {
??? public Mandolin( string serialNumber, double price, MandolinSpec spec)
??????? : base (serialNumber, price, spec) {
??? }
}

以及InstrumentSpec和GuitarSpec、MandolinSpec類(lèi):

public abstract class InstrumentSpec : IEquatable<InstrumentSpec> {
??? private Builder builder;?????? // 廠(chǎng)商
??? private string model;????????? // 型號(hào)
??? private SoundType type;??????? // 類(lèi)型
??? private Wood backWood;???????? // 后部材質(zhì)
??? private Wood topWood;????????? // 前面材質(zhì)

??? // 構(gòu)造函數(shù)和屬性略

??? public bool Equals(InstrumentSpec other) {
??????? string model = other.Model.ToLower();
??????? if (! String .IsNullOrEmpty(model) &&
??????????? ! model.Equals( this .model.ToLower()))
??????????? return false ;
??????? if (builder != other.Builder)
??????????? return false ;
??????? if (type != other.Type)
??????????? return false ;
??????? if (backWood != other.BackWood)
??????????? return false ;
??????? if (topWood != other.TopWood)
??????????? return false ;

??????? return true ;
??? }
}

public class GuitarSpec :InstrumentSpec, IEquatable<GuitarSpec> {
??? private int numStrings;

??? public GuitarSpec(Builder builder, string model, SoundType type, Wood backWood, Wood topWood, int numStrings)
??????? : base (builder, model,type,backWood, topWood) {
??????? this .numStrings = numStrings;
??? }

??? public int NumStrings{
??????? get { return numStrings; }
??? }

??? public bool Equals(GuitarSpec other) {
??????? if (! base .Equals(other))
??????????? return false ;
??????? if (numStrings != other.NumStrings)
??????????? return false ;

??????? return true ;
??? }
}

public class MandolinSpec : InstrumentSpec, IEquatable<MandolinSpec> {

??? private Style style;

??? public MandolinSpec(Builder builder, string model, SoundType type, Wood backWood, Wood topWood, Style style)
??????? : base (builder, model, type, backWood, topWood) {
??????? this .style = style;
??? }

??? public bool Equals(MandolinSpec other) {
??????? if (! base .Equals(other))
??????????? return false ;
??????? if (style != other.style)
??????????? return false ;

??????? return true ;
??? }
}

最后,我們需要修改Inventory類(lèi):

public class Inventory {
??? private List<Instrument> instrumentList; // 維護(hù)現(xiàn)有的所有樂(lè)器

??? public Inventory() {
??????? instrumentList = new List <Instrument>();
??? }

??? // Search() 和 AddInstrument()方法見(jiàn)下
}

我們通過(guò)抽象和繼承完成了程序的擴(kuò)展?,F(xiàn)在來(lái)看一下上面實(shí)現(xiàn)的擴(kuò)展性如何,為了更簡(jiǎn)單地對(duì)問(wèn)題進(jìn)行描述,我們?cè)O(shè)想如果再加入一種樂(lè)器,班卓琴(Banjo),程序需要做哪些改動(dòng)?

1、我們需要再定義一個(gè)繼承自Instrument的類(lèi)Banjo;

2、以及一個(gè)繼承自InstrumentSpec的類(lèi)BanjoSpec;此時(shí),如果BanjoSpec擁有InstrumentSpec沒(méi)有定義的屬性,那么很好辦,我們?cè)贐anjoSpec中添加新增的屬性即可;如果BanjoSepc不需要InstrumentSpec中定義的屬性,比如說(shuō)Model,那么就麻煩了,我們需要從InstrumentSpec中刪掉此屬性,然后再在InstrumentSpec除了BanjoSpec以外的所有子類(lèi)中添加剛才刪去的Model屬性。

3、我們還需要修改Inventory的AddInstrument()方法:

// 向列表中添加 樂(lè)器
public void AddInstrument( string serialNumber, double price, InstrumentSpec spec) {
??? Instrument instrument = null ;
??? if (spec is GuitarSpec){
??????? instrument = new Guitar (serialNumber, price, (GuitarSpec)spec);
??? } else if (spec is MandolinSpec) {
??????? instrument = new Mandolin (serialNumber, price, (MandolinSpec)spec);
??? }
??? instrumentList.Add(instrument);
}

這里,因?yàn)镮nstrument是抽象類(lèi),所以我們無(wú)法創(chuàng)建Instrument的實(shí)例,只能創(chuàng)建其子類(lèi)的實(shí)例,而Guitar和Mandolin的構(gòu)造函數(shù),分別需要InstrumentSpec的子類(lèi)(GuitarSpec和MandolinSpec),所以我們需要先進(jìn)行向下轉(zhuǎn)換((GuitarSpec)spec),才能創(chuàng)建對(duì)象。

4、類(lèi)似地,我們也需要修改Search()方法:

// 搜索列表,尋找滿(mǎn)足SearchSpec參數(shù)的樂(lè)器
public List<Instrument> Search(InstrumentSpec searchSpec) {
??? List<Instrument>. Enumerator it = instrumentList.GetEnumerator();

??? List<Instrument> list = new List <Instrument>();
??? MandolinSpec mandolinSpec;
??? GuitarSpec guitarSpec;
??? while (it.MoveNext()) {
??????? if (it.Current is Guitar && searchSpec is GuitarSpec) {
??????????? guitarSpec = (GuitarSpec)it.Current.Spec;
??????????? if (guitarSpec.Equals((GuitarSpec)searchSpec))
??????????????? list.Add(it.Current);
??????? } else if (it.Current is Mandolin && searchSpec is MandolinSpec) {
??????????? mandolinSpec = (MandolinSpec)it.Current.Spec;
??????????? if (mandolinSpec.Equals((MandolinSpec)searchSpec))
??????????????? list.Add(it.Current);
??????? }
??? }

??? return list;
}

我們看到,盡管只是添加一種樂(lè)器,不僅需要對(duì)多處進(jìn)行修改,而且還要再添加兩個(gè)新類(lèi)Banjo和BanjoSpec。設(shè)想如果有10多種樂(lè)器,那么改動(dòng)及類(lèi)的數(shù)量都會(huì)是非常多的,維護(hù)起來(lái)也會(huì)像是噩夢(mèng)一般。那么下來(lái)該再如何改進(jìn)呢?我們接著往下看。

4.動(dòng)態(tài)屬性

首先我們看一下Guitar、Mandolin和Banjo類(lèi),它們除了構(gòu)造函數(shù)不同以外其余完全相同。而一般情況下,我們定義一個(gè)抽象類(lèi)和子類(lèi)這種繼承體系,目的是 為了在基類(lèi)中實(shí)現(xiàn)一種行為,然后在各個(gè)子類(lèi)中對(duì)其進(jìn)行重寫(xiě),以實(shí)現(xiàn)多態(tài)的效果。 所以,此處我們可以考慮另外一種方式,將Instrument聲明為實(shí)例的,并且在其中加入一個(gè)枚舉類(lèi)型的屬性InstrumentType,由這個(gè)屬性來(lái)標(biāo)識(shí)樂(lè)器的類(lèi)別。以后我們需要添加新的類(lèi)型,只需要在這個(gè)枚舉中添加就可以了:

// 樂(lè)器類(lèi)型
public enum InstrumentType {
??? Guitar = 0, Mandolin, Banjo
}

因?yàn)镮nstrumentType和SerialNumber、Price一樣,屬于每種樂(lè)器都有的屬性,所以我們將它定義在Instrument類(lèi)中,而非InstrumentSpec中,此時(shí)Instument我們也聲明為一般類(lèi),而非抽象類(lèi):

public class Instrument {
??? private InstrumentType type;?? // 樂(lè)器類(lèi)型

??? // 其余略...
}

對(duì)于InstrumentSpec類(lèi)及其子類(lèi)而言,由于屬性是多變的,而基類(lèi)并沒(méi)有定義抽象或者虛擬方法供子類(lèi)覆蓋,所以我們可以使用一個(gè)Hashtable將樂(lè)器的屬性值按照 key/value 的形式保存起來(lái),其中 key是屬性名稱(chēng),value是屬性值。這樣就可以刪去所有的InstrumentSpec的子類(lèi)(GuitarSpec、MandolinSepc等),同時(shí),我們將InstrumentSpec聲明為一般類(lèi):

public class InstrumentSpec : IEquatable<InstrumentSpec> {

??? private Hashtable properties;

??? public InstrumentSpec(Hashtable properties) {
??????? if (properties == null )
??????????? properties = new Hashtable ();
??????? else
??????????? this .properties = properties;
??? }

??? public Hashtable Properties {
??????? get { return properties; }
??? }

??? public Object GetProperty( object propertyName) {
??????? return properties[propertyName];
??? }

??? public bool Equals(InstrumentSpec other) {
??????? IEnumerator it = other.properties.Keys.GetEnumerator();
??????? while (it.MoveNext()) {
??????????? if (properties[it.Current] != other.properties[it.Current])
??????????????? return false ;
??????? }

??????? return true ;
??? }
}

通過(guò)上面的改變,我們添加新樂(lè)器時(shí),只需要改變枚舉就可以了,而不需要再添加大量的諸如Guitar和GuitarSpec這樣的子類(lèi)。

最后我們?cè)倏匆幌翴nventory類(lèi)的實(shí)現(xiàn):

public class Inventory {
??? private List<Instrument> instrumentList; // 維護(hù)現(xiàn)有的所有樂(lè)器

??? public Inventory() {
??????? instrumentList = new List <Instrument>();
??? }

??? // 向列表中添加 樂(lè)器
??? public void AddInstrument( string serialNumber, double price, InstrumentSpec spec) {
??????? Instrument instrument = new Instrument (serialNumber, price, spec);
??????? instrumentList.Add(instrument);
??? }

??? // 搜索列表,尋找滿(mǎn)足SearchSpec參數(shù)的樂(lè)器
??? public List<Instrument> Search(InstrumentSpec searchSpec) {
??????? List<Instrument>. Enumerator it = instrumentList.GetEnumerator();
??????? List<Instrument> list = new List <Instrument>();

??????? while (it.MoveNext()) {
??????????? if (it.Current.Spec.Equals(searchSpec))
??????????????? list.Add(it.Current);
??????? }
??????? return list;
??? }
}

可以看到Inventory類(lèi)也變得清爽了許多。那么采用這種方式是不是就最好了呢?我們?nèi)匀灰吹剿膯?wèn)題:

  1. 盡管將屬性和屬性值保存在Hashtable中極大的增加了靈活性,但是我們每次構(gòu)建對(duì)象,為對(duì)象添加屬性值也會(huì)變得非常繁瑣。
  2. Hashtable返回的是一個(gè)Object類(lèi)型的對(duì)象,所以我們?cè)讷@得到屬性之后,還需要再進(jìn)行一次向下轉(zhuǎn)換才行。
  3. 同樣,因?yàn)镠ashtable可以接收任何類(lèi)型的對(duì)象,所以我們也就喪失了類(lèi)型安全,比如說(shuō),對(duì)于一個(gè)只可以接受int類(lèi)型的屬性,我們可以輸入任意值而在編譯時(shí)不會(huì)報(bào)錯(cuò),只有在運(yùn)行時(shí),我們將值取出進(jìn)行向下轉(zhuǎn)化時(shí)才會(huì)拋出異常。

所以說(shuō),設(shè)計(jì)并沒(méi)有最好,只有最合適的,本文討論的也是一樣,我們只能根據(jù)實(shí)際情況,選擇最合適的解決方案。對(duì)于 只有一種樂(lè)器、支持多種樂(lè)器、樂(lè)器屬性變化不大、屬性變化很大等各種不同情況,我們需要做出權(quán)衡,選擇合適的解決方案。另外在實(shí)現(xiàn)時(shí)還要做出一定的預(yù)見(jiàn),考慮以后某方面的變更會(huì)不會(huì)很大,然后再考慮需不需要留出擴(kuò)展的余地。對(duì)于一個(gè)系統(tǒng),我們很可能 設(shè)計(jì)不足 ,也有可能 過(guò)度設(shè)計(jì) 。我覺(jué)得,我們應(yīng)該首先具備了 過(guò)度設(shè)計(jì) 的能力,然后再去考慮哪些地方不需要過(guò)度“靈活”,因?yàn)橥ǔC糠N設(shè)計(jì)都有著自身的優(yōu)點(diǎn)和缺陷,很難找到一種絕對(duì)正確的方案

一個(gè)逐步“優(yōu)化”的范例程序


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦?。?!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 六月色播 | 日韩一区二区三区在线看 | 美腿丝袜亚洲综合 | 九一国产在线观看免费 | 久草视频观看 | 日韩精品网址 | 日韩欧美在线观看 | 秋霞日韩久久理论电影 | 欧美在线资源 | 欧美最新一区二区三区四区 | 波多野结衣一区二区三区88 | 欧美18videosex性欧美群 | 欧美激情 在线 | 欧美久久xxxxxx影院 | 欧美在线看| 欧美一级黄色影院 | 91影院 | 久久丝袜视频 | 日本不卡一区 | 欧美一区二区三区免费高 | 久久久久久久久99精品 | 精品国产一区二区三区免费 | 亚洲一区在线日韩在线深爱 | 一区二区福利视频 | 九九激情网 | 亚洲精品久 | 欧美视频第二页 | 99欧美精品 | 日韩欧美在线观看视频 | 久久这里只有精品国产99 | 日本一道在线 | 日韩欧美综合在线 | 日本wwxx | 色噜噜狠狠色综合久 | 欧美影院久久 | 欧美18—19sex性hd按摩 | 都市妖奇谈 电视剧 | 日本三级理论 | 三人弄娇妻高潮3p视频 | 中国大陆高清aⅴ毛片 | www嫩草|