开放封闭原则(OCP,Open Closed Principle)是面向对象原则的核心。由于软件设计本身所追求的墓边就是封装变化,降低耦合,而开放封闭原则就是对这一目标的直接体现。(你必须知道的.NET p48页)

以下用例子说明,同样来自该书。

假设在一个柜台业务处理系统,首先有客户:

  1. public class Client
  2. {
  3. public string Name{get;set;}
  4. private string ClientType { get; set; }
  5. }
  1. ClientType表示客户的类型,如"存款用户""转账用户""取款用户"
  1. public class BusyBankStaff
  2. {
  3. private readonly BankProcess bankProcess = new BankProcess();
  4. public void HandleProcess(Client client)
  5. {
  6. switch (client.ClientType)
  7. {
  8. case "存款用户":
  9. bankProcess.Deposit();
  10. break;
  11. case "转账用户":
  12. bankProcess.Transfer();
  13. break;
  14. case "取款用户":
  15. bankProcess.DrawMoney();
  16. break;
  17. }
  18. }
  19. }

业务处理类

  1. public class BankProcess
  2. {
  3. public void Deposit() { }
  4. public void Transfer() { }
  5. public void DrawMoney() { }
  6. }

问题是,如果银行多了一种业务类型,比如代购公积金,那么,必然地,BankProcess要修改为

  1. public class BankProcess
  2. {
  3. public void Deposit() { }
  4. public void Transfer() { }
  5. public void DrawMoney() { }
  6. public void BuyFund(){}
  7. }

,并且BusyBankStaff中坏味道的switch语句又要增加一个条件。

其实,这样类的设计违反了开放封闭原则。

所谓开放封闭,是指,对修改封闭——一个类一旦写好,就不能再修改;对扩展开放——如果有新的需求,可以在不修改原系统的基础上方便增加。

怎么改?抽象!对可能或经常变化的部分使用接口将其封装。在此例中,BusyBankStaff依赖于BankProcess类,将其改为依赖于一个IBankProcess接口

  1. public interface IBankProcess
  2. {
  3. void Process();
  4. }

然后,不同的业务类型都实现该接口

  1. public class DepositProcess:IBankProcess
  2. {
  3. public void Process(){}
  4. }
  1. public class TransferProcess:IBankProcess
  2. {
  3. public void Process(){}
  4. }

等等。

此时BusyBankStaff可以摇身一变,变成EasyBankStaff

  1. public class EasyBankStaff
  2. {
  3. private readonly IBankProcess bankProcess = new BankProcess();
  4. public void HandleProcess(Client client)
  5. {
  6. bankProcess=client.CreateProcess();
  7. bankProcess.Process();
  8. }
  9. }

注意到,业务的分配由银行的业务员转为客户,让客户自己依据自己的类型创建相应的bankProcess对象——这就是现实中客户依据业务类型的不同在排队取号机前取不同的业务号码。

现在,若新增一个业务,只要增加一个实现IBankProcess接口的类即可。

  1. public class BuyFund:IBankProcess
  2. {
  3. public void Process(){}
  4. }

大功告成了吗?不!

若观察系统,我们会发现客户类此时已经转变为

  1. public class Client
  2. {
  3. public string Name{get;set;}
  4. private string ClientType { get; set; }
  5.  
  6. public IBankProcess CreateProcess()
  7. {
  8. switch(clientType)
  9. {
  10. case"存款用户":
  11. return new DepositProcess();
  12. case "转账用户":
  13. return new TransferProcess();
  14.  
  15. }
  16. }
  17. }

又是一个违反OCP的类,又是switch的坏味道。

怎么办?同样是抽象!


将client类抽象为接口

  1. public interface IClient
  2. {
  3. IBankProcess CreateProcess();
  4. }

然后不同类型的客户都实现这一接口

  1. public class DepositClient:Client
  2. {
  3. IBankProcess CreateProcess
  4. {
  5. return new DepositProcess();
  6. }
  7.  
  8. }
  1. public class TransferClient:Client
  2. {
  3. IBankProcess CreateProcess
  4. {
  5. return new TransferProcess();
  6. }
  7.  
  8. }

如果有新的客户类型,那么就新增一个这样的类即可。完成!

《你必须知道的.NET》书中对OCP(开放封闭)原则的阐述的更多相关文章

  1. 必须知道的Spring Boot中的一些Controller注解

    这篇文章是抄其他人的,原址:https://cloud.tencent.com/developer/article/1082720 本文旨在向你介绍在Spring Boot中controller中最基 ...

  2. [你必须知道的.NET]第二十六回:认识元数据和IL(下)

    发布日期:2009.03.04 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回: 第二十四回:认识元数据和IL(上), ...

  3. MVC中你必须知道的13个扩展点

    MVC中你必须知道的13个扩展点 pasting 转:http://www.cnblogs.com/kirinboy/archive/2009/06/01/13-asp-net-mvc-extensi ...

  4. 解惑《你必须知道的.net》——C#继承关系中【方发表】的创建和调用

    前言: 现在正在读<你必须知道的.net>(第二版)一书,看到IL语言那一章,将call.callvirt和calli时候,书中举了一个例子,是一个三层继承的例子,我一开始看的时候就有点懵 ...

  5. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

  6. 每个项目中,你必须知道的11个Java第三方类库。

    Java第三方library ecosystem是一个很广阔的范畴.不久前有人撰文:每个项目中,你必须知道的11个Java第三方类库. 单元测试 1.DBUnit DBunit是一个基于junit扩展 ...

  7. [你必须知道的.NET]第三十回:.NET十年(下)

    发布日期:2009.05.11 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文部分内容,已 ...

  8. [你必须知道的.NET]第二十九回:.NET十年(上)

    发布日期:2009.05.08 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文部分内容,已 ...

  9. 从零开始学习jQuery(剧场版) 你必须知道的javascript

    原文:从零开始学习jQuery(剧场版) 你必须知道的javascript 一.摘要 本文是jQuery系列教程的剧场版, 即和jQuery这条主线无关, 主要介绍大家平时会忽略的一些javascri ...

随机推荐

  1. Pyqt 中__init__(self,parent==None) parent理解

    参考: 在PyQt中,所有class都是从QObject派生而来,QWidget对象就可以有一个parent.这种parent-child关系主要用于两个方面: 没有parent的QWidget类被认 ...

  2. DDD的思考

    概述 DDD领域驱动设计,它是对面向对象的的分析和设计(OOAD,Object Orient Analysis Design)的一个补充,对技术框架进行了分层规划,同时对每个类进行了策略和类型划分.领 ...

  3. org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance:

    详细错误堆栈信息: org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" ...

  4. java Integer和int的拆箱与装箱

    官网:http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html 1.赋值: a. 把int类型赋值给Integer类型:JVM会 ...

  5. 遍历PspCidTable表检测隐藏进程

    一.PspCidTable概述 PspCidTable也是一个句柄表,其格式与普通的句柄表是完全一样的,但它与每个进程私有的句柄表有以下不同: 1.PspCidTable中存放的对象是系统中所有的进程 ...

  6. 数字信号处理实验(五)——IIR滤波器的设计

    一.使用自编函数设计IIR滤波器 1.冲激响应法 (1)注给出的数字滤波器指标先化成模拟指标 (2)设计出模拟滤波器: (3)使用冲激响应法转化成数字滤波器 (4)一个demo clear all; ...

  7. Android加载大图片OOM异常解决

      尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图, 因为这些函数在完成decode后,最终都是通过 ...

  8. EditText监听键盘输入

    第一步,先在布局中为EditText设置属性 <EditText android:singleLine="true" android:imeOptions="act ...

  9. 查看mysql的安装信息

    查看mysql的安装信息: #ps -ef | grep mysql usr/bin/mysql 是指:mysql的运行路径 var/lib/mysql 是指:mysql数据库文件的存放路径 usr/ ...

  10. java jdbc sqlhelper

    package com.shop.util; import java.sql.*; //SqlHelper类 //定义了数据库连接函数,关闭查询结果集,关闭Statement对象,关闭数据库连接 // ...