開(kāi)放封閉原則:
軟件實(shí)體(類、模塊跨晴、函數(shù)等等)應(yīng)該可以擴(kuò)展欧聘,但是不可以修改。
也就是如果需求發(fā)生變化導(dǎo)致程序中多個(gè)依賴模塊都發(fā)生了級(jí)聯(lián)的改動(dòng)端盆,就說(shuō)明這個(gè)程序是有問(wèn)題的怀骤,程序變得相對(duì)脆弱、無(wú)法重用焕妙。開(kāi)放封閉原則就相對(duì)的解決了這個(gè)問(wèn)題蒋伦,它強(qiáng)調(diào)的是你設(shè)計(jì)的模塊應(yīng)該從不改變(絕對(duì)不改變是不可能的,只能相對(duì)少改動(dòng))焚鹊。當(dāng)需求變化時(shí)痕届,你可以通過(guò)添加新的代碼來(lái)擴(kuò)展這個(gè)模塊的行為,而不去更改那些已經(jīng)存在的可以工作的代碼。
舉一個(gè)簡(jiǎn)單的開(kāi)放封閉原則的例子:書(shū)店售書(shū)打折研叫。
書(shū)店周年慶打折售書(shū)锤窑,40塊以上的書(shū)打7折,40塊以下的書(shū)打8折嚷炉。
那我們?nèi)绾问褂瞄_(kāi)放封閉原則來(lái)實(shí)現(xiàn)打折售書(shū)的這個(gè)情況呢渊啰。
首先,我們將正常的售書(shū)程序?qū)懗鰜?lái)渤昌。
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 開(kāi)放封閉原則
/// </summary>
namespace OpenAndClosed
{
/// <summary>
/// 主程序
/// </summary>
class Program
{
public static List<Book> bookList = new List<Book>();
static void Main(string[] args)
{
if (bookList.Count == 0)
{
bookList.Add(new Book("劉同", "誰(shuí)的青春不迷茫", 35));
bookList.Add(new Book("劉慈欣", "三體", 55));
bookList.Add(new Book("王小波", "黃金時(shí)代", 41));
}
for (int i = 0; i < bookList.Count; i++)
{
Console.WriteLine("作者:"+ book.GetAuthor() +"; 書(shū)名:"+ book.GetBookName() + "; 價(jià)格:"+ book.GetPrice(bookList[i].price)); }
Console.ReadLine();
}
}
}
書(shū)類:book.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
/// <summary>
/// 書(shū)的類
/// </summary>
public class Book
{
/// <summary>
/// 作者
/// </summary>
public string author;
/// <summary>
/// 書(shū)名
/// </summary>
public string bookName;
/// <summary>
/// 價(jià)格
/// </summary>
public double price;
/// <summary>
/// 構(gòu)造函數(shù)
/// </summary>
/// <param name="author">作者</param>
/// <param name="bookName">書(shū)名</param>
/// <param name="price">價(jià)錢(qián)</param>
public Book(string author,string bookName, double price)
{
this.author = author;
this.bookName = bookName;
this.price = price;
}
/// <summary>
/// 獲取作者
/// </summary>
/// <returns></returns>
public string GetAuthor()
{
return author;
}
/// <summary>
/// 獲取作者
/// </summary>
/// <returns></returns>
public double GetPrice(double pri)
{
price = pri;
return price;
} /// <summary>
/// 獲取作者
/// </summary>
/// <returns></returns>
public string GetBookName()
{
return bookName;
}
}
}
現(xiàn)在來(lái)考慮虽抄,如何實(shí)現(xiàn)上邊所說(shuō)的打折。
第一種方法:在主函數(shù)中(program.cs)判斷價(jià)格独柑,符合要求打折。
但是這顯然不合適私植,如果打折的需求變得更加復(fù)雜忌栅,或者以后需要增加積分返點(diǎn)之類的功能,這部分代碼就變得相當(dāng)?shù)挠纺[曲稼。
第二種方法:在實(shí)現(xiàn)類(Book.cs)中判斷價(jià)格索绪,符合要求打折。
這個(gè)很明顯的違反了開(kāi)放-封閉原則贫悄。
如果修改這個(gè)類中g(shù)etPrice方法瑞驱,那摩一定要在這個(gè)方法中進(jìn)行價(jià)格判斷,就算把每個(gè)打折的算法封裝成多個(gè)子類窄坦,每次增加或者修改打折算法的時(shí)候唤反,都需要去修改這個(gè)方法中的判斷。
第三種方法:定義一個(gè)類鸭津,繼承Book.cs彤侍,再類中覆蓋父類中的getPrice方法。
這樣我們就可以不修改Book類中g(shù)etPrice及主函數(shù)的代碼來(lái)實(shí)現(xiàn)打折的功能逆趋。
下邊是使用開(kāi)放封閉原則來(lái)實(shí)現(xiàn)的代碼:
主文件:program.cs :改為實(shí)例化book類的子類discount
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// 開(kāi)放封閉原則
/// </summary>
namespace OpenAndClosed
{
/// <summary>
/// 主程序
/// </summary>
class Program
{
public static List<Book> bookList = new List<Book>();
static void Main(string[] args)
{
if (bookList.Count == 0)
{
bookList.Add(new Book("劉同", "誰(shuí)的青春不迷茫", 35));
bookList.Add(new Book("劉慈欣", "三體", 55));
bookList.Add(new Book("王小波", "黃金時(shí)代", 41));
}
// Book book = null;
Discount discount = null;
for (int i = 0; i < bookList.Count; i++)
{
// 將此處修改為實(shí)例化子類
//book = new Book(bookList[i].author,bookList[i].bookName,bookList[i].price);
discount = new Discount(bookList[i].author, bookList[i].bookName, bookList[i].price);
// book = new Book(bookList[i].author,bookList[i].bookName,bookList[i].price);
Console.WriteLine("作者:"+ discount.GetAuthor() +"; 書(shū)名:"+ discount.GetBookName() + "; 價(jià)格:"+ discount.GetPrice(bookList[i].price));
}
Console.ReadLine();
}
}
}
Book.cs 書(shū)類(未做修改)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
/// <summary>
/// 書(shū)的類
/// </summary>
public class Book
{
/// <summary>
/// 作者
/// </summary>
public string author;
/// <summary>
/// 書(shū)名
/// </summary>
public string bookName;
/// <summary>
/// 價(jià)格
/// </summary>
public double price;
/// <summary>
/// 構(gòu)造函數(shù)
/// </summary>
/// <param name="author">作者</param>
/// <param name="bookName">書(shū)名</param>
/// <param name="price">價(jià)錢(qián)</param>
public Book(string author,string bookName, double price)
{
this.author = author;
this.bookName = bookName;
this.price = price;
}
/// <summary>
/// 獲取作者
/// </summary>
/// <returns></returns>
public string GetAuthor()
{
return author;
}
/// <summary>
/// 獲取作者
/// </summary>
/// <returns></returns>
public double GetPrice(double pri)
{
price = pri;
return price;
}
/// <summary>
/// 獲取作者
/// </summary>
/// <returns></returns>
public string GetBookName()
{
return bookName;
}
}
}
Discount.cs價(jià)格打折文件盏阶,繼承book.cs(book.cs的擴(kuò)展)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
public class Discount : Book
{
/// <summary>
/// 作者
/// </summary>
public static string author;
/// <summary>
/// 書(shū)名
/// </summary>
public static string bookName;
/// <summary>
/// 價(jià)格
/// </summary>
public static double price;
/// <summary>
/// 構(gòu)造函數(shù)
/// </summary>
/// <param name="author">作者</param>
/// <param name="bookName">書(shū)名</param>
/// <param name="price">價(jià)錢(qián)</param>
public Discount(string autho, string bookNam, double pric): base(author, bookName, price)
{
price = pric;
author = autho;
bookName = bookNam;
}
public double GetPrice(double pri)
{
double total;
Strategy stra;
if (price > 40)
{
stra = new Strategy(new seven(), price);
}
else if (price < 40)
{
stra = new Strategy(new eight(), price);
}
else
{
stra = new Strategy(new Normal(), price);
}
total = stra.returnDouble();
return total;
}
}
}
策略類:Strategy.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
public class Strategy
{
public IBook ibook = null;
public double price;
public Strategy(IBook book, double price)
{
ibook = book;
this.price = price;
}
public double returnDouble()
{
return ibook.getBookPrice(price);
}
}
}
基類:IBook.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
public interface IBook
{
double getBookPrice(double price);
}
}
子類:八折類:eight.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
public class eight:IBook
{
public double getBookPrice(double price)
{
return price * 0.8;
}
}
}
七折類:seven.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
public class seven:IBook
{
public double getBookPrice(double price)
{
return price * 0.7;
}
}
}
正常價(jià)格類:Normal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenAndClosed
{
public class Normal:IBook
{
public double getBookPrice(double price)
{
return price;
}
}
}
這里使用開(kāi)放封閉原則的話,就是增加了一個(gè)打折類discount.cs闻书,繼承自Book類名斟,是book類的擴(kuò)展。在整改調(diào)用魄眉,調(diào)用的時(shí)候調(diào)用子類的對(duì)象就行了砰盐。代碼中都有體現(xiàn)。注意一下就好杆融。
剩下的我結(jié)合之前看過(guò)的策略模式楞卡,定義了一個(gè)策略類,將打折算法封裝成一個(gè)算法家族。為以后再次增加新的打折算法提供便利蒋腮。
開(kāi)放封閉原則淘捡,是最為重要的設(shè)計(jì)原則,里式替換原則和合成/聚合復(fù)用原則為開(kāi)放-封閉原則提供保證池摧。
可以通過(guò)模板方法模式和策略模式進(jìn)行重構(gòu)焦除,實(shí)現(xiàn)對(duì)修改封閉,對(duì)擴(kuò)展開(kāi)放的設(shè)計(jì)思路作彤。
封裝變化膘魄,是實(shí)現(xiàn)開(kāi)放封閉原則的重要手段,對(duì)于經(jīng)常發(fā)生變化的狀態(tài)竭讳,一般將其封裝為一個(gè)抽象创葡,例如上邊例子中的IBook接口。
拒絕濫用抽象绢慢,只將經(jīng)常變化的部分進(jìn)行抽象灿渴。
最后,放上設(shè)計(jì)六大設(shè)計(jì)原則
有好的建議胰舆,請(qǐng)?jiān)谙路捷斎肽愕脑u(píng)論骚露。