开放封闭原则:
软件实体(类、模块、函数等等)应该可以扩展,但是不可以修改。
也就是如果需求发生变化导致程序中多个依赖模块都发生了级联的改动,就说明这个程序是有问题的,程序变得相对脆弱、无法重用。开放封闭原则就相对的解决了这个问题,它强调的是你设计的模块应该从不改变(绝对不改变是不可能的,只能相对少改动)。当需求变化时,你可以通过添加新的代码来扩展这个模块的行为,而不去更改那些已经存在的可以工作的代码。
举一个简单的开放封闭原则的例子:书店售书打折。
书店周年庆打折售书,40块以上的书打7折,40块以下的书打8折。那我们如何使用开放封闭原则来实现打折售书的这个情况呢。
首先,我们将正常的售书程序写出来。
Program.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; /// <summary> /// 开放封闭原则 /// </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("刘同", "谁的青春不迷茫", 35)); bookList.Add(new Book("刘慈欣", "三体", 55)); bookList.Add(new Book("王小波", "黄金时代", 41)); } for (int i = 0; i < bookList.Count; i++) { Console.WriteLine("作者:"+ book.GetAuthor() +"; 书名:"+ book.GetBookName() + "; 价格:"+ book.GetPrice(bookList[i].price)); } Console.ReadLine(); } } }
书类:book.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OpenAndClosed { /// <summary> /// 书的类 /// </summary> public class Book { /// <summary> /// 作者 /// </summary> public string author; /// <summary> /// 书名 /// </summary> public string bookName; /// <summary> /// 价格 /// </summary> public double price; /// <summary> /// 构造函数 /// </summary> /// <param name="author">作者</param> /// <param name="bookName">书名</param> /// <param name="price">价钱</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; } } }
现在来考虑,如何实现上边所说的打折。
第一种方法:在主函数中(program.cs)判断价格,符合要求打折。
但是这显然不合适,如果打折的需求变得更加复杂,或者以后需要增加积分返点之类的功能,这部分代码就变得相当的臃肿。
第二种方法:在实现类(Book.cs)中判断价格,符合要求打折。
这个很明显的违反了开放-封闭原则。
如果修改这个类中getPrice方法,那摩一定要在这个方法中进行价格判断,就算把每个打折的算法封装成多个子类,每次增加或者修改打折算法的时候,都需要去修改这个方法中的判断。
第三种方法:定义一个类,继承Book.cs,再类中覆盖父类中的getPrice方法。
这样我们就可以不修改Book类中getPrice及主函数的代码来实现打折的功能。
下边是使用开放封闭原则来实现的代码:
主文件:program.cs :改为实例化book类的子类discount
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; /// <summary> /// 开放封闭原则 /// </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("刘同", "谁的青春不迷茫", 35)); bookList.Add(new Book("刘慈欣", "三体", 55)); bookList.Add(new Book("王小波", "黄金时代", 41)); } // Book book = null; Discount discount = null; for (int i = 0; i < bookList.Count; i++) { // 将此处修改为实例化子类 //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() +"; 书名:"+ discount.GetBookName() + "; 价格:"+ discount.GetPrice(bookList[i].price)); } Console.ReadLine(); } } }
Book.cs 书类(未做修改)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace OpenAndClosed { /// <summary> /// 书的类 /// </summary> public class Book { /// <summary> /// 作者 /// </summary> public string author; /// <summary> /// 书名 /// </summary> public string bookName; /// <summary> /// 价格 /// </summary> public double price; /// <summary> /// 构造函数 /// </summary> /// <param name="author">作者</param> /// <param name="bookName">书名</param> /// <param name="price">价钱</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价格打折文件,继承book.cs(book.cs的扩展)
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> /// 书名 /// </summary> public static string bookName; /// <summary> /// 价格 /// </summary> public static double price; /// <summary> /// 构造函数 /// </summary> /// <param name="author">作者</param> /// <param name="bookName">书名</param> /// <param name="price">价钱</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; } } }
正常价格类: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; } } }
这里使用开放封闭原则的话,就是增加了一个打折类discount.cs,继承自Book类,是book类的扩展。在整改调用,调用的时候调用子类的对象就行了。代码中都有体现。注意一下就好。
剩下的我结合之前看过的策略模式,定义了一个策略类,将打折算法封装成一个算法家族。为以后再次增加新的打折算法提供便利。
开放封闭原则,是最为重要的设计原则,里式替换原则和合成/聚合复用原则为开放-封闭原则提供保证。
可以通过模板方法模式和策略模式进行重构,实现对修改封闭,对扩展开放的设计思路。
封装变化,是实现开放封闭原则的重要手段,对于经常发生变化的状态,一般将其封装为一个抽象,例如上边例子中的IBook接口。
拒绝滥用抽象,只将经常变化的部分进行抽象。
最后,放上设计六大设计原则