zoukankan      html  css  js  c++  java
  • .net面向对象设计原则

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

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

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

           一、单一职责原则

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

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

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

            应用如下:

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

            

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

            

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

     1     /// <summary>
     2     /// 数据库操作实体
     3     /// </summary>
     4     public class DBAction : IDBAction
     5     {
     6         /// <summary>
     7         /// 增加
     8         /// </summary>
     9         public void Add()
    10         {
    11             Console.WriteLine("Add Succeed");
    12         }
    13         /// <summary>
    14         /// 删除
    15         /// </summary>
    16         /// <returns></returns>
    17         public int Delete()
    18         {
    19             Console.WriteLine("Delete Succeed");
    20             return 1;
    21         }
    22         /// <summary>
    23         /// 查询页面
    24         /// </summary>
    25         public void View()
    26         {
    27         }
    28     }
    DBAction类
     1      /// <summary>
     2     /// 权限判断和数据操作代理类
     3     /// </summary>
     4     public class DBManagerProxy : IDBAction
     5     {
     6         private IDBAction _dbManager = null; 
     7         public DBManagerProxy(IDBAction dbManager)
     8         {
     9             _dbManager = dbManager;
    10         }
    11 
    12         /// <summary>
    13         /// 获取权限
    14         /// </summary>
    15         /// <param name="id"></param>
    16         /// <returns></returns>
    17         public string GetPermission()
    18         {
    19             return "CanAdd,CanDelete";
    20         }
    21 
    22         /// <summary>
    23         /// 添加
    24         /// </summary>
    25         public void Add()
    26         {
    27             if (GetPermission().Contains("CanAdd"))
    28             {
    29                 _dbManager.Add();
    30             }
    31         }
    32         /// <summary>
    33         /// 删除
    34         /// </summary>
    35         /// <returns></returns>
    36         public int Delete()
    37         {
    38             int result = 0;
    39             if (GetPermission().Contains("CanDelete"))
    40             {
    41                 result = _dbManager.Delete();
    42             }
    43 
    44             return result;
    45         }
    46         /// <summary>
    47         /// 查询页面
    48         /// </summary>
    49         public void View()
    50         {
    51         }
    52     }
    DBManagerProxy类
     1   public class DBClient
     2     {
     3         public static void Main()
     4         {
     5 
     6             IDBAction dBAction = new DBManagerProxy(new DBAction());
     7             dBAction.Add();
     8             dBAction.Delete();
     9 
    10             Console.ReadKey();
    11         }
    12     }
    DBClient类
     1  /// <summary>
     2     /// 操作数据库接口
     3     /// </summary>
     4     public interface IDBAction
     5     {
     6         /// <summary>
     7         /// 增加
     8         /// </summary>
     9         void Add();
    10         /// <summary>
    11         /// 删除
    12         /// </summary>
    13         /// <returns></returns>
    14         int Delete();
    15         /// <summary>
    16         /// 查询页面
    17         /// </summary>
    18         void View();
    19     }
    IDBAction类

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

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

           二、开放封闭原则

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

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

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

          实际应用如下:

           

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

           

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

     1     /// <summary>
     2     /// 机场买票窗口接口
     3     /// </summary>
     4     public interface IAirportTicketProcess
     5     {
     6         /// <summary>
     7         /// 业务处理
     8         /// </summary>
     9         void Process();
    10     }
    IAirportTicketProcess类
     1     /// <summary>
     2     /// 买票类
     3     /// </summary>
     4     public class CollectTicketProcess : IAirportTicketProcess
     5     {
     6         /// <summary>
     7         /// 买票业务
     8         /// </summary>
     9         public void Process()
    10         {
    11             Console.WriteLine("只接受买票业务");
    12         }
    13     }
    CollectTicketProcess类
     1     /// <summary>
     2     /// 查询票务类
     3     /// </summary>
     4     public class ReferTicketProcess : IAirportTicketProcess
     5     {
     6         /// <summary>
     7         /// 查询票务业务
     8         /// </summary>
     9         public void Process()
    10         {
    11             Console.WriteLine("只接受票务查询业务");
    12         }
    13     }
    ReferTicketProcess类
     1     /// <summary>
     2     /// 退票类
     3     /// </summary>
     4     public class RefoundTicketProcess : IAirportTicketProcess
     5     {
     6         /// <summary>
     7         /// 退票业务
     8         /// </summary>
     9         public void Process()
    10         {
    11             Console.WriteLine("只接受退票业务");
    12         }
    13     } 
    RefoundTicketProcess类
     1     /// <summary>
     2     /// 业务员类
     3     /// </summary>
     4     public class AirportStaff
     5     {
     6         private IAirportTicketProcess _airportTicketProcess = null; 
     7         /// <summary>
     8         /// 具体业务员操作的业务类
     9         /// </summary>
    10         public void HandleProcess(Client client)
    11         {
    12             _airportTicketProcess = client.CreateProcess();
    13             _airportTicketProcess.Process();
    14         }
    15     }
    AirportStaff类
     1     /// <summary>
     2     /// 客户类
     3     /// </summary>
     4     public class Client
     5     {
     6         private string _clientType;
     7         public Client(string clientType)
     8         {
     9             _clientType = clientType;
    10         }
    11         /// <summary>
    12         /// 客户业务处理判断
    13         /// </summary>
    14         /// <returns></returns>
    15         public IAirportTicketProcess CreateProcess()
    16         {
    17             switch (_clientType)
    18             {
    19                 case "查询客户":
    20                     return new ReferTicketProcess();
    21                 case "买票客户":
    22                     return new CollectTicketProcess();
    23                 case "退票客户":
    24                     return new RefoundTicketProcess();
    25             }
    26 
    27             return null;
    28         }
    29     }
    Client类
     1  public class AirportProcess
     2     {
     3         public static void Main()
     4         {
     5 
     6             AirportStaff staff = new AirportStaff();
     7             staff.HandleProcess(new Client("买票客户"));
     8             staff.HandleProcess(new Client("查询客户"));
     9             staff.HandleProcess(new Client("退票客户"));
    10 
    11             Console.ReadKey();
    12         }
    13     }
    AirportProcess类

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

           

     1     /// <summary>
     2     /// 挂失处理
     3     /// </summary>
     4     public class LossTicketProcess : IAirportTicketProcess
     5     {
     6         /// <summary>
     7         /// 挂失处理
     8         /// </summary>
     9         public void Process()
    10         {
    11             Console.WriteLine("只接受挂失业务");
    12         }
    13     }
    LossTicketProcess类

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

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

           三、依赖倒置原则

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

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

          实际应用如下:

          

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

          

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

    1     /// <summary>
    2     /// 客户接口
    3     /// </summary>
    4     public interface IClient
    5     {
    6         IAirportTicketProcess CreateProcess();
    7     }
    IClient接口
     1     /// <summary>
     2     /// 买票客户
     3     /// </summary>
     4     public class CollectTicketClient : IClient
     5     {
     6         public IAirportTicketProcess CreateProcess()
     7         {
     8             return new CollectTicketProcess();
     9         }
    10     }
    CollectTicketClient类
     1     /// <summary>
     2     /// 票务查询客户
     3     /// </summary>
     4     public class ReferTicketClient : IClient
     5     {
     6         public IAirportTicketProcess CreateProcess()
     7         {
     8             return new ReferTicketProcess();
     9         }
    10     } 
    ReferTicketClient类
     1     /// <summary>
     2     /// 退票客户
     3     /// </summary>
     4     public class ReFoundTicketClient : IClient
     5     {
     6         public IAirportTicketProcess CreateProcess()
     7         {
     8             return new RefoundTicketProcess();
     9         }
    10     }
    ReFoundTicketClient类
     1    public class AirportStaffNew
     2     {
     3         private IAirportTicketProcess _airportTicketProcess = null;
     4         /// <summary>
     5         /// 具体业务员操作的业务类
     6         /// </summary>
     7         public void HandleProcess(IClient client)
     8         {
     9             _airportTicketProcess = client.CreateProcess();
    10             _airportTicketProcess.Process();
    11         }
    12     }
    AirportStaffNew类
     1     public class AirportProcessNew
     2      {
     3         public static void Main()
     4         {
     5 
     6             AirportStaffNew staff = new AirportStaffNew();
     7             staff.HandleProcess(new ReFoundTicketClient());
     8             staff.HandleProcess(new ReferTicketClient());
     9 
    10             Console.ReadKey();
    11         }
    12     }
    AirportProcessNew类

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

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

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

        四、接口隔离原则

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

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

         实际应用如下:

          

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

           

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

           五、Liskov替换原则

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

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

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

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

         六、合成/聚合复用原则

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

         七、迪米特法则

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

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

  • 相关阅读:
    win10系统封装
    docker基础知识
    TCP三次握手顺手看个示例
    磁盘挂载
    jQuary
    docker rpm包安装
    Mysql单机安装
    docker网络模式
    JavaScript DOM
    JavaScript作用域,面向对象
  • 原文地址:https://www.cnblogs.com/yiliukejich0614-1234/p/10345730.html
Copyright © 2011-2022 走看看