[转]Object Oriented Design Principles

Who is Audience?

This article is intended for those who have at least a basic idea of Object oriented programming. They know the difference between classes and objects and can talk about the basic pillars(支柱) of object oriented programming i.e., Encapsulation, Abstraction, Polymorphism and Inheritance(继承).

[转]Object Oriented Design Principles_第1张图片

Introduction

In the object oriented world we only see objects. Objects interact with each other. Classes, Objects, Inheritance, Polymorphism, Abstraction are common vocabulary we hear in our day-to-day careers.

In the modern software world every software developer uses object oriented language of some kind, but the question is, does he really know what object oriented programming means? Does he know that he is working as an object oriented programmer? If the answer is yes, is he really using the power of object oriented programming?

In this article we will go beyond(超过) the basic pillars of object oriented programming and talk about object oriented design.

Object Oriented Design

It’s a process of planning a software system where objects will interact(相互作用,交互) with each other to solve specific(特定的) problems. The saying goes, "Proper Object oriented design makes a developer's life easy, whereas(然而) bad design makes it a disaster."

How does anyone start?

When anyone starts creating software architecture(架构) their intentions are good. They try to use their existing experience to create an elegant(高雅的) and clean design.

[转]Object Oriented Design Principles_第2张图片


Over time software starts to rot(腐烂,腐败). With every feature request or change software design alters its shape and eventually(终于) the simplest changes to application requires a lot of effort and, more importantly, creates higher chances for more bugs.

[转]Object Oriented Design Principles_第3张图片

Who is to Blame

Software solves real life business problems and since business processes keep evolving(进化), software keeps on changing.

Changes are an integral(基本的,不可缺少的) part of the software world. Obviously(显然) because clients are paying they will demand for what they are expecting. So we cannot blame "change" for the degradation of software design. It is our design which is at fault.

One of the biggest reasons for the damaging(破坏) of software design is the introduction of unplanned dependencies(无计划的依赖) into the system. Every part of the system is dependant on some other part and so changing one part will affect another part. If we are able to manage those dependencies we will easily maintain the software system and software quality too.

Example

[转]Object Oriented Design Principles_第4张图片

Solution - Principles, Design Patterns and Software architecture

  • Software architecture like MVC, 3-Tier, MVP tells us how overall(总体) projects are going to be structured. 注重于总体
  • Design pattern allows us to reuse the experience or rather, provides reusable(重用) solutions to commonly occurring problems. Example – an object creation problem, an instance management problem(实例管理问题), etc. 关注具体问题
  • Principles tell us, do these and you will achieve this. How you will do it is up to you. Everyone defines some principles in their lives like, "I never lie," or, "I never drink alcohol(酒精)," etc. He/she follow these principles to make his/her life easy, but how will he/she stick to these principles is up to the individual. 提供的是指导方针,是关于哲学中世界观和方法论的问题
注意:这里对软件架构,设计模式和规则(或者原理)的讲解很好,三者之间的区别在于着眼点不同,所处理的层次的也不同。三者的直接的也是紧密联系相互影响的。最简单的例子就是架构模式(综合了软件架构和设计模式)。只所以搞得这么复杂的原因只有一个提高效率,减少成本。由此产生的是各种复用,例如时间复用,空间复用,经验复用---设计模式。

In the same way, object oriented design is filled with many principles which let us manage the problems with software design.

Mr. Robert Martin (commonly known as Uncle Bob) categorized them as

  1. Class Design principles – Also called SOLID
  2. Package Cohesion(内聚) Principles
  3. Package Coupling(藕合) principle

In this article we will talk about SOLID principles with practical example.

SOLID

It’s an acronym(缩写) of J five principles introduced by Mr. Robert Martin (commonly known as Uncle Bob) i.e., Single responsibility(单一原则), Open-closed(开闭原则), Liskov substitution(里氏替换原则), Interface segregation(接口分离原则) and Dependency inversion(依赖倒置或者依赖反转). It’s said (Wikipedia) when all five principles are applied together intend to make it more likely that a programmer will create a system that is easy to maintain and extend over time. Let’s talk about every principle in detail

I) S - SRP - Single responsibility Principle

Real world comparison

[转]Object Oriented Design Principles_第5张图片

I work as a team leader for one of the software firms(公司) in India(印度-注:原作者是印度人). In my spare time I do some writing, newspaper editing and other various projects. Basically, I have multiple(多重) responsibilities in my life.

When something bad happens at my work place, like when my boss scolds(责骂) me for some mistake, I get distracted(转移,分心) from my other work. Basically, if one thing goes bad, everything will mess up(陷入困境).

Identify Problem in Programming

Before we talk about this principle I want you take a look at following class.

[转]Object Oriented Design Principles_第6张图片
  • Every time insert logic changes, this class will change.
  • Every time report format changes, this class will changes.

What is the issue?

Every time one gets changed there is a chance that the other also gets changed because both are staying in the same home and both have same parent. We can’t control everything. So a single change leads to double testing (or maybe more).

What is SRP?

SRP says "Every software module should have only one reason to change".

  • Software Module – Class, Function etc.
  • Reason to change - Responsibility

Solutions which will not Violate(违反) SRP

Now it’s up to us how we achieve this. One thing we can do is create three different classes

  1. Employee – Contains Properties (Data)
  2. EmployeeDB – Does database operations
  3. EmplyeeReport – Does report related tasks
public class Employee
{
    public string EmployeeName { get; set; }
    public int EmployeeNo { get; set; }
}
public class EmployeeDB
{
    public void Insert(Employee e) 
    {
        //Database Logic written here
    }
 public Employee Select() 
    {
        //Database Logic written here
    }
}
public class EmployeeReport
{
    public void GenerateReport(Employee e)
    {
        //Set report formatting
    }
}

Note: This principle also applies to methods. Every method should have a single responsibility.

Can a single class can have multiple methods?

The answer is YES. Now you might ask how it’s possible that

  1. A class will have single responsibility.
  2. A method will have single responsibility.
  3. A class may have more than one method.

Well the answer for this question is simple. It’s context(上下文). Here, responsibility is related to the context in which we are speaking. When we say class responsibility it will be somewhat at higher level(). For instance, the EmployeeDBclass will be responsible for employee operations related to the Database whereas the EmployeeReport class will be responsible for employee operations related to reports.

When it comes to methods it will be at lower level. For instance look at following example:

//Method with multiple responsibilities – violating SRP
public void Insert(Employee e)
{     
    string StrConnectionString = "";       
    SqlConnection objCon = new SqlConnection(StrConnectionString); 
    SqlParameter[] SomeParameters=null;//Create Parameter array from values
    SqlCommand objCommand = new SqlCommand("InertQuery", objCon);
    objCommand.Parameters.AddRange(SomeParameters);
    ObjCommand.ExecuteNonQuery();
}

//Method with single responsibility – follow SRP
public void Insert(Employee e)
{            
    SqlConnection objCon = GetConnection();
    SqlParameter[] SomeParameters=GetParameters();
    SqlCommand ObjCommand = GetCommand(objCon,"InertQuery",SomeParameters);
    ObjCommand.ExecuteNonQuery();
}

private SqlCommand GetCommand(SqlConnection objCon, string InsertQuery, SqlParameter[] SomeParameters)
{
    SqlCommand objCommand = new SqlCommand(InsertQuery, objCon);
    objCommand.Parameters.AddRange(SomeParameters);
    return objCommand;
}

private SqlParameter[] GetParaeters()
{
    //Create Paramter array from values
}

private SqlConnection GetConnection()
{
    string StrConnectionString = "";
    return new SqlConnection(StrConnectionString);
}

Testing is advantageous in and of itself, but that the code has become readable is an additional advantage. The more the code is readable the simpler it seems.

II) O - OCP – Open Close Principle

Real World Comparison

[转]Object Oriented Design Principles_第7张图片

Let’s assume you want to add one more floor between the first and second floor in your two floor house. Do you think it is possible? Yes it is, but is it feasible(可行)? Here are some options:

  • One thing you could have done at time you were building the house first time was make it with three floors, keeping second floor empty. Then utilize(利用) the second floor anytime you want. I don’t know how feasible that is, but it is one solution.
  • Break the current second floor and build two new floors, which is not sensible(明智,明显的).

Identify Problem in Programming

Let's say the Select method in the EmployeeDB class is used by two clients/screens. One is made for normal employees, one is made for managers, and the Manager Screen needs a change in the method.

[转]Object Oriented Design Principles_第8张图片

If I make changes in the Select method to satisfy(满足) the new requirement, other UI will also be affected. Plus making changes in existing tested solution may result in unexpected errors.

What is OCP?

It says, "Software modules should be closed for modifications but open for extensions." An orthogonal statement.

Solution which will not violate OCP

1) Use of inheritance(继承)

We will derive a new class called EmployeeManagerDB from EmployeeDB and override the Select method as per the new requirement.

public class EmployeeDB
{      
    public virtual Employee Select()
    {
        //Old Select Method
    }
}
public class EmployeeManagerDB : EmployeeDB
{
    public override Employee Select()
    {
        //Select method as per Manager
        //UI requirement
    }
}

Note: Now the design is considered good object oriented design if this change is anticipated(期望) at the time of the design and already has a provision(准备,规定) within for extension (method made virtual). Now the UI code will look like:

//Normal Screen
EmployeeDB objEmpDb = new EmployeeDB();
Employee objEmp = objEmpDb.Select();

//Manager Screen
EmployeeDB objEmpDb = new EmployeeManagerDB();
Employee objEmp = objEmpDb.Select();

2) Extension method

If you are using .NET 3.5 or later then there is a second way called extension method that will let us add new methods to existing types without even touching them.

Note: There may be some more ways to achieve the desired(想要的) result. As I said these are principles not commandments(戒律,法令).

III) L – LSP – Liskov substitution principle

What is LSP?

You might be wondering why we are defining it prior to examples and problem discussions. Simply put, I thought it will be more sensible(敏感的) here.

It says, "Subclasses should be substitutable(替代) for base classes." Don’t you think this statement is strange? If we can always write BaseClass b=new DerivedClass() then why would such a principle be made?

Real World Comparison

[转]Object Oriented Design Principles_第9张图片 A father is a real estate(房地产) business man whereas his son wants to be cricketer(板球运动员).

A son can’t replace his father in spite of the fact that they belong to same family hierarchy. 

Identify Problem in Programming

Let’s talk about a very common example.

Normally when we talk about geometric(几何) shapes, we call a rectangle a base class for square. Let’s take a look at code snippet(代码片段).

public class Rectangle
{
    public int Width { get; set; }
    public int Height { get; set; }
}

public class Square:Rectangle
{
    //codes specific to
    //square will be added
}

One can say,

Rectangle o = new Rectangle();
o.Width = 5;
o.Height = 6;

Perfect but as per LSP we should be able to replace Rectangle with square. Let’s try to do so.

Rectangle o = new Square();
o.Width = 5;
o.Height = 6;

What is the matter? Square cannot have different width and height.

What it means? It means we can’t replace base with derived. Means we are violating LSP.

Why don’t we make width and height virtual in Rectangle, and override them in Square?

Code snippet

public class Square : Rectangle 
{
    public override int Width
    {
        get{return base.Width;}
        set
        {
            base.Height = value;
            base.Width = value;
        }
    }
    public override int Height
    {
        get{return base.Height;}
        set
        {
            base.Height = value;
            base.Width = value;
        }
    }        
}

We can’t because doing so we are violating LSP, as we are changing the behavior of Width and Height properties in derived class (for Rectangle height and width cannot be equal, if they are equal it’s cannot be Rectangle).

(It will not be a kind of replacement).

Solution which will not violate LSP

There should be an abstract class Shape which looks like:

public abstract class Shape
{
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }
}

Now there will be two concrete classes independent of each other, one rectangle and one square, both of which will be derived from Shape.

Now the developer can say:

Shape o = new Rectangle();
o.Width = 5;
o.Height = 6;

Shape o = new Square();
o.Width = 5; //both height and width become 5
o.Height = 6; //both height and width become 6

Even after overriding in derived classes we are not changing the behavior of width and height, because when we talk about shape, there will not be any fixed rule for width and height. They may be equal, or may not be.

IV) I – ISP– Interface Segregation principle

Real World Comparison

Let’s say you purchase(购买) a new desktop PC. You will find a couple of(成对的) USB ports, some serial ports, a VGA port etc. If you open the cabinet(橱柜,这里表示的是机箱) you will see lots of slots on the motherboard(母板) used for connecting various(各种各样的) parts with each other, mostly used by hardware engineers(硬件工程师) at the time of assembly.

Those internal slots will not be visible(可见的,现有的) until you open the cabinet. In short, only the required interfaces are made available/visible to you. Imagine(想像) a situation where everything was external(表面) or internal(内部). Then there is a greater chances of hardware failure (as if life wasn't hard enough for computer users).


Let’s say we will go to a shop to buy something (let’s say, for instance, to buy a cricket bat-板球球拍).

Now imagine a situation where the shopkeeper starts showing you the ball and stumps(假肢?板球是啥玩意,跟假肢有毛线关系) as well. It may be possible that we will get confused and may end up buying something we did not require. We may even forget why we were there in the first place.

Identify Problem in Programming

Let’s say we want to develop a Report Management System. Now, the very first task is creating a business layer which will be used by three different UIs.

  1. EmployeeUI – Show reports related to currently logged in employee
  2. ManagerUI – Show reports related to himself and the team for which he/manager belongs.
  3. AdminUI – Show reports related to individual employee ,related to team and related to company like profit(利润) report.
public interface IReportBAL
{
    void GeneratePFReport();
    void GenerateESICReport();

    void GenerateResourcePerformanceReport();
    void GenerateProjectSchedule();

    void GenerateProfitReport();
}
public class ReportBAL : IReportBAL
{    
    public void GeneratePFReport()
    {/*...............*/}

    public void GenerateESICReport()
    {/*...............*/}

    public void GenerateResourcePerformanceReport()
    {/*...............*/}

    public void GenerateProjectSchedule()
    {/*...............*/}

    public void GenerateProfitReport()
    {/*...............*/}
}
public class EmployeeUI
{
    public void DisplayUI()
    {
        IReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
    }
}
public class ManagerUI
{
    public void DisplayUI()
    {
        IReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport ();
        objBal.GenerateProjectSchedule ();
    }
}
public class AdminUI
{
    public void DisplayUI()
    {
        IReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport();
        objBal.GenerateProjectSchedule();
        objBal.GenerateProfitReport();
    }
}

Now in each UI when the developer types "objBal" the following intellisense(智能感应) will be shown:

[转]Object Oriented Design Principles_第10张图片

What is the problem?

The developer who is working on EmployeeUI gets access to all the other methods as well, which may unnecessarily cause him/her confusion.

What is ISP?

It states that "Clients should not be forced to implement interfaces they don’t use." It can also be stated as "Many client specific interfaces are better than one general purpose interface." In simple words, if your interface is fat, break it into multiple interfaces.

Update code to follow ISP

 Collapse | Copy Code
public interface IEmployeeReportBAL
{
    void GeneratePFReport();
    void GenerateESICReport();
}
public interface IManagerReportBAL : IEmployeeReportBAL
{
    void GenerateResourcePerformanceReport();
    void GenerateProjectSchedule();
}
public interface IAdminReportBAL : IManagerReportBAL
{
    void GenerateProfitReport();
}
public class ReportBAL : IAdminReportBAL 
{    
    public void GeneratePFReport()
    {/*...............*/}

    public void GenerateESICReport()
    {/*...............*/}

    public void GenerateResourcePerformanceReport()
    {/*...............*/}

    public void GenerateProjectSchedule()
    {/*...............*/}

    public void GenerateProfitReport()
    {/*...............*/}
}

[转]Object Oriented Design Principles_第11张图片
public class EmployeeUI
{
    public void DisplayUI()
    {
        IEmployeeReportBAL objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
    }
}
[转]Object Oriented Design Principles_第12张图片
public class ManagerUI
{
    public void DisplayUI()
    {
        IManagerReportBAL  objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport ();
        objBal.GenerateProjectSchedule ();
    }
}
[转]Object Oriented Design Principles_第13张图片
public class AdminUI
{
    public void DisplayUI()
    {
        IAdminReportBAL  objBal = new ReportBAL();
        objBal.GenerateESICReport();
        objBal.GeneratePFReport();
        objBal.GenerateResourcePerformanceReport();
        objBal.GenerateProjectSchedule();
        objBal.GenerateProfitReport();
    }
}

By following ISP we can make client see, what he is required to see.

V) D – DIP– Dependency Inversion principle 

Real World Comparison

Let’s talk about our desktop computers. Different parts such as RAM, a hard disk, and CD-ROM (etc.) are loosely(松的) connected to the motherboard. That means that, if, in future in any part stops working it can easily be replaced with a new one. Just imagine a situation where all parts were tightly(紧的) coupled to each other, which means it would not be possible to remove any part from the motherboard. Then in that case if the RAM stops working we have to buy new motherboard which is going to be very expensive.

Identify Problem in Programming

Look at the following code.

public class CustomerBAL
{
    public void Insert(Customer c)
    {
        try
        {
            //Insert logic
        }
        catch (Exception e)
        {
            FileLogger f = new FileLogger();
            f.LogError(e);
        }
    }
}

public class FileLogger
{
    public void LogError(Exception e)
    {
        //Log Error in a physical file
    }
}

In the above code CustomerBAL is directly dependent on the FileLogger class which will log(记录) exceptions in physical file. Now let’s assume tomorrow management decides to log exceptions in the Event Viewer. Now what? Change existing code. Oh no! My God, that might create a new error!

What is DIP?

It says, "High level modules should not depend upon low level modules. Rather, both should depend upon abstractions."

Solution with DIP

public interface ILogger
{
    void LogError(Exception e);
}

public class FileLogger:ILogger
{
    public void LogError(Exception e)
    {
        //Log Error in a physical file
    }
}
public class EventViewerLogger : ILogger
{
    public void LogError(Exception e)
    {
        //Log Error in a physical file
    }
}
public class CustomerBAL
{
    private ILogger _objLogger;
    public CustomerBAL(ILogger objLogger)
    {
        _objLogger = objLogger;
    }

    public void Insert(Customer c)
    {
        try
        {
            //Insert logic
        }
        catch (Exception e)
        {            
            _objLogger.LogError(e);
        }
    }
}

As you can see the client depends on abtraction i.e, ILogger which can be set to an instance of any derived class.

So now we've covered all five principles of SOLID. Thanks Uncle Bob.

Is it end?

Now the question is, are there more principles(原则) other than those categorized(分类) by Uncle Bob? The answer is yes, but we will not going to describe each and every thing in detail for now. But they are:

  • Program to Interface Not Implementation. --- 面向接口编程而不是具体实现
  • Don't Repeat Yourself. -- 重用代码
  • Encapsulate What Varies. --哪里变化封装哪里(大概就是这个意思,英语不是很好)
  • Depend on Abstractions, Not Concrete classes. --依赖抽象而是具体的类(其实和面向接口编程一个意思)
  • Least Knowledge Principle. -- 最少知识原则 --loosely coupling
  • Favor Composition over Inheritance. -- 优先使用组合而不是集成
  • Hollywood Principle. --传说中好莱坞原则(don‘t call us, we‘ll call you),这个在操作系统中应用的比较多,比如说著名的回调函数
  • Apply Design Pattern wherever possible. --尽可能的应用设计模式
  • Strive for Loosely Coupled System.--努力构建松耦合系统
  • Keep it Simple and Sweet / Stupid. -- KISS-Unix哲学的铁律,这个著名的没法说了。详细可以参考《UNIX编程艺术》(不是所有的书都可以称为神作的,这本书可以的)

Conclusion

We can’t avoid changes. The only thing we can do is develop and design software in such a way that it is able to handle such changes.

  • SRP should be kept in mind while creating any class, method or any other module (which even applies to SQL stored procedures and functions). It makes code more readable, robust, and testable. 
  • As per my experience we can’t follow DIP each and every time, sometimes we have to depend on concrete classes. The only thing we have to do is understand the system, requirements and environment properly and find areas where DIP should be followed. 
  • Following DIP and SRP will opens a door to implement OCP as well. 
  • Make sure to create specific interfaces so that complexities and confusions will be kept away from end developers, and thus, the ISP will not get violated.
  • While using inheritance take care of LSP. 

Hope all of you enjoyed reading this article. Thank you for the patience.

以上是原文,费了好大的劲转换到CSDN的博客上,各种图片上传,各种失败。个人觉得还是原来的博客的排版和布局好看一点,不信可以对比,简直可以用惨不忍睹目不能视来形容。

除了直接复制原文以外,有些单词我加了注释的,原因很简单:六级未过!!回想我学习英语的漫漫历史,唉,往事不堪回首往事,回首尽是辛酸泪。不管这么样,由于原文还是比较简单的英语,借助有道词典,我总算还是把它看懂了,虽然花了很长时间。英语的确是硬伤啊!!

本文的原来的出处是CodeProject,并且原文章是有版权的。在某个月黑风高的夜晚,我用着我的24k的火眼金睛找查找某个XXX资源的时候,偶尔浏览到CodeProject的网页,当时就觉的CodeProject老牛逼的,于是乎就随即注册了个账号。后来发现,每天都有邮件推送,当时也不是很忙,就每天都看了看,于是乎看到了这篇文章,于是有了这篇转载。

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

转自:Object Oriented Design Principleshttp://www.codeproject.com/Articles/567768/Object-Oriented-Design-Principles

你可能感兴趣的:(设计模式,C#)