稳定的框架来源于好的设计,好的设计才能出好的作品,掌握面向对象基本原则才会使我们的设计灵活、合理、不僵化,今天就来谈一谈我们.net 面向对象设计的基本原则。

对于一个没有任何设计经验的开发者来说,如果不假思索和探究式的去设计系统软件的框架,势必会导致系统代码出现这样或者那样的问题,比如:代码复杂和重复,不能剥离出独立的复用组件,系统不稳定等。通过灵活的设计原则,加上一定的设计模式,封装变化,降低耦合,实现软件的复用和扩展,这正是设计原则的最终意义。

我们都知道面向对象的三大要素是封装、继承和多态,这三大完整体系以抽象来封装变化,隐藏具体实现,保护内部信息;继承实现复用;多态改写对象行为。在此基础上我们来聊一聊.net中七大面向对象设计原则:

 一、单一职责原则

    系统好不好,强调的是模块间保持低耦合、高内聚的关系,面向对象设计原则第一条则是:单一职责原则(SRP)。

核心:单一职责原则强调的是职责分离,一个类应该只有一种引起它变化的原因,不应该将变化原因不同的职责封装在一起。

实现单一职责原则最好一个类只做一件事,职责过多,可能引起它变化的原因就很多,这将导致职责依赖,相互之间就会产生影响。

应用如下:

举一个项目中经常用到的例子,比如通过不同的权限来设计数据库不同的操作。

以上的设计中,把数据库的操作类和用户权限的判断封装在一个类中,设计就显得有点僵硬,因为权限的规则变化和数据库的规则变化都会改变DBManager类。好的设计是需要将二者分开,设计如下:

新增类DBManagerProxy,有效实现了职责分离,类DBAction至关注数据库操作,不用担心权限判断,使用职责分离的代码如下:

     /// <summary>
/// 数据库操作实体
/// </summary>
public class DBAction : IDBAction
{
/// <summary>
/// 增加
/// </summary>
public void Add()
{
Console.WriteLine("Add Succeed");
}
/// <summary>
/// 删除
/// </summary>
/// <returns></returns>
public int Delete()
{
Console.WriteLine("Delete Succeed");
return ;
}
/// <summary>
/// 查询页面
/// </summary>
public void View()
{
}
}

DBAction类

      /// <summary>
/// 权限判断和数据操作代理类
/// </summary>
public class DBManagerProxy : IDBAction
{
private IDBAction _dbManager = null;
public DBManagerProxy(IDBAction dbManager)
{
_dbManager = dbManager;
} /// <summary>
/// 获取权限
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public string GetPermission()
{
return "CanAdd,CanDelete";
} /// <summary>
/// 添加
/// </summary>
public void Add()
{
if (GetPermission().Contains("CanAdd"))
{
_dbManager.Add();
}
}
/// <summary>
/// 删除
/// </summary>
/// <returns></returns>
public int Delete()
{
int result = ;
if (GetPermission().Contains("CanDelete"))
{
result = _dbManager.Delete();
} return result;
}
/// <summary>
/// 查询页面
/// </summary>
public void View()
{
}
}

DBManagerProxy类

   public class DBClient
{
public static void Main()
{ IDBAction dBAction = new DBManagerProxy(new DBAction());
dBAction.Add();
dBAction.Delete(); Console.ReadKey();
}
}

DBClient类

  /// <summary>
/// 操作数据库接口
/// </summary>
public interface IDBAction
{
/// <summary>
/// 增加
/// </summary>
void Add();
/// <summary>
/// 删除
/// </summary>
/// <returns></returns>
int Delete();
/// <summary>
/// 查询页面
/// </summary>
void View();
}

IDBAction类

       优点:模块间保持低耦合、高内聚的关系。

       建议:SRP应该由引起变化的原因决定,而不是功能所决定的,这个需要自己在设计的过程中慎重权衡。

 二、开放封闭原则

开放封闭原则(OCP)是面向对象原则的核心,软件本身就是封装变化,降低耦合,而开放封闭原则就是这一目标的直接体现。

核心:对扩展开放,对修改关闭。

在实际系统开发过程中需求是千变万化的,不是一成不变的,所以在一定程度上我们要满足系统本身的可扩展性,这就要求我们在设计能够灵活的扩展。实现这一原则的核心思想就是对抽象编程,而不是对具体编程。让类依赖于固定的抽象,所以对修改就是关闭的;通过对对象的继承和多态机制,实现新的扩展,所以是对扩展是开放的。

实际应用如下:

以上设计是机场售票业务,AirportStaff是业务员处理的业务类,AirportTicketProcess是客户信息类,设计中每个售票员都会面对买票、退票和咨询问题的客户,这样售票员不仅繁忙而且效率低下,在设计上更是拙劣,不利于扩展,为了更好地扩展,改造如下:

IAirportTicketProcess是抽象出来的机场业务接口,不管你是退票、查询还是买票都是业务的一种类型,那么我们只需继承该接口实现不同的业务即可,即便有新增业务类型,只需要继承该接口,仍然不影响原有设计,对于客户来说,根据自己的需求找对应的业务员处理,这样设计清晰职责单一。代码实现如下:

     /// <summary>
/// 机场买票窗口接口
/// </summary>
public interface IAirportTicketProcess
{
/// <summary>
/// 业务处理
/// </summary>
void Process();
}

IAirportTicketProcess类

     /// <summary>
/// 买票类
/// </summary>
public class CollectTicketProcess : IAirportTicketProcess
{
/// <summary>
/// 买票业务
/// </summary>
public void Process()
{
Console.WriteLine("只接受买票业务");
}
}

CollectTicketProcess类

     /// <summary>
/// 查询票务类
/// </summary>
public class ReferTicketProcess : IAirportTicketProcess
{
/// <summary>
/// 查询票务业务
/// </summary>
public void Process()
{
Console.WriteLine("只接受票务查询业务");
}
}

ReferTicketProcess类

     /// <summary>
/// 退票类
/// </summary>
public class RefoundTicketProcess : IAirportTicketProcess
{
/// <summary>
/// 退票业务
/// </summary>
public void Process()
{
Console.WriteLine("只接受退票业务");
}
}

RefoundTicketProcess类

     /// <summary>
/// 业务员类
/// </summary>
public class AirportStaff
{
private IAirportTicketProcess _airportTicketProcess = null;
/// <summary>
/// 具体业务员操作的业务类
/// </summary>
public void HandleProcess(Client client)
{
_airportTicketProcess = client.CreateProcess();
_airportTicketProcess.Process();
}
}

AirportStaff类

     /// <summary>
/// 客户类
/// </summary>
public class Client
{
private string _clientType;
public Client(string clientType)
{
_clientType = clientType;
}
/// <summary>
/// 客户业务处理判断
/// </summary>
/// <returns></returns>
public IAirportTicketProcess CreateProcess()
{
switch (_clientType)
{
case "查询客户":
return new ReferTicketProcess();
case "买票客户":
return new CollectTicketProcess();
case "退票客户":
return new RefoundTicketProcess();
} return null;
}
}

Client类

  public class AirportProcess
{
public static void Main()
{ AirportStaff staff = new AirportStaff();
staff.HandleProcess(new Client("买票客户"));
staff.HandleProcess(new Client("查询客户"));
staff.HandleProcess(new Client("退票客户")); Console.ReadKey();
}
}

AirportProcess类

这种设计无论对业务员还是客户一切都变得简单而有序,如果新增挂失业务,只需要继承接口新增具体挂失类即可

     /// <summary>
/// 挂失处理
/// </summary>
public class LossTicketProcess : IAirportTicketProcess
{
/// <summary>
/// 挂失处理
/// </summary>
public void Process()
{
Console.WriteLine("只接受挂失业务");
}
}

LossTicketProcess类

       优点:有效降低实体与实体之间的耦合度;对容易变化的因素进行抽象处理,从而改善类的内聚性。

       建议:封装变化是实现OCP的重要思路,对于经常发生变化的状态一般将其封装为一个抽象,同时拒绝乱用抽象,只将经常变化的部分进行抽象。

三、依赖倒置原则

核心:依赖存在于类与类、模块与模块之间,核心是依赖于抽象,具体体现在高层模块不应该依赖于底层模块,二者都应该依赖于接口, 抽象不应该依赖于具体,具体应该依赖于对象。缩写DIP

当两个模块发生紧耦合的关系时,最好的办法就是分离接口和实现:在依赖之间定义一个接口,使得高层模块调用接口,底层模块实现接口,达到依赖与抽象的目的。

实际应用如下:

在上述机场售票业务中,还有个不足之处就是在AirportStaff业务员处理的业务类依赖于客户类Client,这样就导致了业务依赖于客户类型,如果新增业务类型,就需要新增对客户的依赖。改造后如下:

可以把客户也抽象一个接口,不同的客户调用对应的业务。代码如下:

     /// <summary>
/// 客户接口
/// </summary>
public interface IClient
{
IAirportTicketProcess CreateProcess();
}

IClient接口

     /// <summary>
/// 买票客户
/// </summary>
public class CollectTicketClient : IClient
{
public IAirportTicketProcess CreateProcess()
{
return new CollectTicketProcess();
}
}

CollectTicketClient类

     /// <summary>
/// 票务查询客户
/// </summary>
public class ReferTicketClient : IClient
{
public IAirportTicketProcess CreateProcess()
{
return new ReferTicketProcess();
}
}

ReferTicketClient类

     /// <summary>
/// 退票客户
/// </summary>
public class ReFoundTicketClient : IClient
{
public IAirportTicketProcess CreateProcess()
{
return new RefoundTicketProcess();
}
}

ReFoundTicketClient类

    public class AirportStaffNew
{
private IAirportTicketProcess _airportTicketProcess = null;
/// <summary>
/// 具体业务员操作的业务类
/// </summary>
public void HandleProcess(IClient client)
{
_airportTicketProcess = client.CreateProcess();
_airportTicketProcess.Process();
}
}

AirportStaffNew类

     public class AirportProcessNew
{
public static void Main()
{ AirportStaffNew staff = new AirportStaffNew();
staff.HandleProcess(new ReFoundTicketClient());
staff.HandleProcess(new ReferTicketClient()); Console.ReadKey();
}
}

AirportProcessNew类

这样AirportStaffNew依赖于接口,更方便扩展。

    优点:系统稳定,降低耦合度。

    建议:依赖于抽象是一个通用的规则,特殊情况也会依赖于细节,必须权衡好抽象和具体的取舍。

四、接口隔离原则

核心:使用多个小的专门接口,不建议使用大的总接口。缩写ISP

这个比较好理解,我就直接上设计。

实际应用如下:

这个接口虽然功能都能实现要求,但是比较臃肿,更好地设计如下:

优点:有效的将抽象和细节分开。
       建议:将功能相近的接口合并,可能造成接口污染,最好使用内聚的接口。

五、Liskov替换原则

又称里氏替换原则,是实现开放封闭原则的具体规范,这个大家只需要做一个了解。

核心:子类必须能够替换其基类。缩写LSP

它主要在继承机制中约束规范,子类必须能够替换其基类才能保证系统在运行期内识别子类,这是保证复用的基础。LSP要求基类是virtual方法那么子类中就必须重写该方法,如果子类有基类没有的方法或者成员都违反LSP原则。

优点:增强系统的扩展性,基于多态的机制能够减少代码冗余。
      建议:子类必须满足基类和客户端对其的行为约定,子类的异常必须控制在基类可以预计的范围内。

六、合成/聚合复用原则

新对象中聚合已有的对象使之成为新对象的成员,少继承、多聚合。缩写CARP。

 七、迪米特法则

     最少知道原则,软件实体应该尽可能少的和其他软件实体发生相互作用。缩写LoD。

以上是我对.NET面向对象设计原则的总结,欢迎纠错。

.net面向对象设计原则的更多相关文章

  1. Java程序员应该了解的10个面向对象设计原则

    面向对象设计原则: 是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorat ...

  2. UML类图与面向对象设计原则

    1. 引言     从大一开始学习编程,到如今也已经有两年了.从最初学习的Html,Js,JaveSe,再到JavaEE,Android,自己也能写一些玩具.学习过程中也无意识的了解了一些所谓的设计模 ...

  3. 【OOAD】面向对象设计原则概述

    软件的可维护性和可复用性 知名软件大师Robert C.Martin认为一个可维护性(Maintainability) 较低的软件设计,通常由于如下4个原因造成: 过于僵硬(Rigidity)  ...

  4. C++ 设计模式2 (面向对象设计原则)

    1. 变化是复用的天敌! 面向对象设计的最大优势在于 : 抵御变化 2. 重新认识面向对象 理解隔离变化: 从宏观层面来看,面向对象的构建方式更能适应软件的变化, 能将变化所带来的影响减为最小. 各司 ...

  5. 面向对象设计原则OO

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  6. (转)Java程序员应该了解的10个面向对象设计原则

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  7. Java程序员应当知道的10个面向对象设计原则

    面向对象设计原则是OOPS编程的核心, 但我见过的大多数Java程序员热心于像Singleton (单例) . Decorator(装饰器).Observer(观察者) 等设计模式,而没有把足够多的注 ...

  8. 【面向对象设计原则】之里氏替换原则(LSP)

    里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,所以使用的是这位女博士的性命名的一个 ...

  9. 【面向对象设计原则】之依赖倒置原则(DIP)

    依赖倒转原则(Dependency Inversion  Principle, DIP):抽象不应该依赖于细节,细节应当依赖于抽象.换言之,要针对抽象(接口)编程,而不是针对实现细节编程. 开闭原则( ...

  10. 【面向对象设计原则】之接口隔离原则(ISP)

    接口隔离原则(Interface  Segregation Principle, ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口. 从接口隔离原则的定义可以看 ...

随机推荐

  1. 从壹开始前后端 [vue后台] 之二 || 完美实现 JWT 滑动授权刷新

    缘起 哈喽大家周一好!不知道小伙伴们有没有学习呀,近来发现各种俱乐部搞起来了,啥时候群里小伙伴也搞一次分享会吧,好歹也是半千了(时间真快,还记得5个月前只有20多人),之前在上个公司,虽然也参与组织过 ...

  2. Spring Boot 中关于自定义异常处理的套路!

    在 Spring Boot 项目中 ,异常统一处理,可以使用 Spring 中 @ControllerAdvice 来统一处理,也可以自己来定义异常处理方案.Spring Boot 中,对异常的处理有 ...

  3. 《HelloGitHub》第 34 期

    公告 新年快乐!大家的年终奖都发了啥? <HelloGitHub>第 34 期 兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源 ...

  4. windows&lunix下node.js实现模板化生成word文件

    最近在做了一个小程序!里面有个功能就是根据用户提交的数据,自动生成一份word文档返回给用户.我也是第一次做这功能,大概思路就是先自己弄一份word模板,后台接受小程序发过来的数据,再根据这些数据将相 ...

  5. 8天入门docker系列 —— 第二天 通过一个aspnetcore程序加深对容器的理解

    我们知道容器是一个打包了应用和相关依赖的盒子,那怎么去操控这个盒子呢? 这一篇我通过一个简单的aspnetcore程序来加深对盒子的理解,使用之前先 安装一下Docker的环境. 一:Docker的安 ...

  6. 虹软人脸识别ArcFace2.0 Android SDK使用教程

    一.获取SDK 1.进入ArcFace2.0的申请地址 https://ai.arcsoft.com.cn/product/arcface.html 2.填写信息申请并提交 申请通过后即可下载SDK, ...

  7. windows设置照片查看器为默认的照片查看软件

    复制一下内容到记事本中: 文件名:PotoView.bat 文件内容: @echo off set reg_dir=hklm\SOFTWARE\Microsoft\Windows Photo View ...

  8. SQL执行WebService

    写了一个钉钉发送消息的类, 要发送用友等审核单据信息, 模式: 钉钉发消息功能在webservice中, 用友消息列表中有新消息时,采用触发器执行webservice. 在测试中 ,功能正常 ,但将在 ...

  9. Postman 安装

    前言 安装前的准备: 1.Chrome 浏览器的扩展插件来进行的安装,并非单独应用程序. 2.电脑上已经安装了 Chrome 浏览器 3.本文章适用操作系统  window7 一,非官方安装 个人不建 ...

  10. 阿里云SLB出现502 Bad Gateway 错误排查解决方法

    502 Bad Gateway The proxy server received an invalid response from an upstream server. 原本系统是通过一个SLB转 ...