常用的面向对象设计原则(C#版)

常用的面向对象设计原则(C#版)

对于面向对象设计来说,在支持代码可维护性的同时,需要提高代码可复用性。复用性可以提高开发效率,提高开发质量,节约开发成本,恰当的复用还可以提升可维护性。

设计原则名称 定义 重要程度
单一职责原则(Single Responsibility Principle, SRP) 一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中 ※※※※
开闭原则(Open-Closed Principle, OCP) 软件实体应当对扩展开放,对修改关闭 ※※※※※
迪米特法则(Law of Demeter, LoD) 每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位 ※※※
依赖倒置原则(Dependency Inversion Principle, DIP) 高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象 ※※※※※
接口隔离原则(Interface Segregation Principle, ISP) 客户端不应该依赖那些它不需要的接口 ※※
合成复用原则(Composite Reuse Principle, CRP) 优先使用对象组合,而不是继承来达到复用的目的 ※※※※
里氏代换原则(Liskov Substitution Principle, LSP) 所有引用基类的地方必须能透明地使用其子类的对象 ※※※※

关注1个类如何设计(使类具有高内聚)

1. 单一职责原则

概括:一个类只负责一个功能领域中的相应职责
优点:类的复杂度降低,提高了复用性
缺点:无法精确地度量每个类的具体职责

违反原则例子

class Study
{
    
    
    //交学费不在学习类的范围当中,违反单一职责原则
    public void PayTuition()
    {
    
    
        Console.WriteLine("交学费");
    }
    public void ReadBook()
    {
    
    
        Console.WriteLine("读书");
    }
    public void WriteCode()
    {
    
    
        Console.WriteLine("写字");
    }
}

符合原则例子

class Study
{
    
    
    public void ReadBook()
    {
    
    
        Console.WriteLine("读书");
    }
    public void WriteCode()
    {
    
    
        Console.WriteLine("写字");
    }
}

//增加支付类专门用来支付各种费用
class PayMoney
{
    
    
    public void PayTuition()
    {
    
    
        Console.WriteLine("交学费");
    }
    public void PayHourseRent()
    {
    
    
        Console.WriteLine("交房租");
    }
}

2. 开闭原则

概括:类、模块、函数等等应该可以扩展,但是不可修改。
优点:更容易扩展,提高了扩展性
缺点:容易导致类、模块、函数等等过多

违反原则例子

class Book
{
    
    
    //如增加一种类型的数据,就要修改这个对象方法

    public void GetBookType(int id)
    {
    
    
        if(id == 0)
        {
    
    
            Console.WriteLine("编程语言");
        }
        else if(id == 1)
        {
    
    
            Console.WriteLine("游戏开发");
        }
    }
}

符合原则例子

abstract class Book
{
    
    
    public abstract void GetBookType();
}

class ProgrammeBook : Book
{
    
    
    public override void GetBookType()
    {
    
    
        Console.WriteLine("编程语言");
    }
}

class GameDevelopmentBook : Book
{
    
    
    public override void GetBookType()
    {
    
    
        Console.WriteLine("游戏开发");
    }
}
//若有新类型书籍,无需修改原对象代码,只需要增加类
class MultiMediaBook : Book
{
    
    
    public override void GetBookType()
    {
    
    
        Console.WriteLine("多媒体");
    }
}

关注类之间关系如何设计(使类之间具有低耦合)

1. 迪米特法则-两个类尽量不要发生关系

概括:要求类应当尽可能少的与其他类发生相互作用
优点:减小类之间的依赖,降低类之间的耦合性
缺点:出现大量方法用来间接调用,造成模块间通信效率降低,不容易协调。

违反原则例子

class Student
{
    
    
    //类中包含其他类
    private Dad myDad = new Dad();

    public void RequestPay()
    {
    
    
        myDad.PayTuition();
    }
}

class Dad
{
    
    
    public void PayTuition()
    {
    
    
        Console.WriteLine("交学费");
    }
}

class School
{
    
    
    //类中包含其他类
    private List<Student> stuList;
    public void WarnPay()
    {
    
    
        foreach(Student stu in stuList)
        {
    
    
            stu.RequestPay();
        }
    }
}

符合原则例子

class Student
{
    
    
    public void RequestPay(Dad myDad)
    {
    
    
        myDad.PayTuition();
    }
}

class Dad
{
    
    
    public void PayTuition()
    {
    
    
        Console.WriteLine("交学费");
    }
}

class School
{
    
    
    //客户端可声明后调用,不再需要对学生集合进行赋值
    public void WarnPay(List<Student> stuList)
    {
    
    
        foreach(Student stu in stuList)
        {
    
    
            stu.RequestPay(new Dad());
        }
    }
}

2. 依赖倒置原则-非要有关系,语法要依赖抽象

概括:要求抽象不应该依赖于细节,细节应该依赖于抽象;要针对接口编程,不要针对接实现编程
优点:提高稳定性,降低代码修改带来的风险
缺点:抽象要以项目为度,一般抽象难度较大

违反原则例子

class Cat
{
    
    
    public void Move()
    {
    
    
        Console.WriteLine("猫会动");
    }
}

class Dog
{
    
    
    public void Move()
    {
    
    
        Console.WriteLine("狗会动");
    }
}

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        //若是Dog类,要重新声明一个对象
        Cat cat1 = new Cat();
        cat1.Move();
        Console.Read();
    }
}

符合原则例子

abstract class Animal
{
    
    
    public abstract void Move();
}

class Cat:Animal
{
    
    
    public override void Move()
    {
    
    
        Console.WriteLine("猫会动");
    }
}

class Dog:Animal
{
    
    
    public override void Move()
    {
    
    
        Console.WriteLine("狗会动");
    }
}

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        //若是Dog类只需重新赋值Dog对象
        Animal an1 = new Cat();
        an1.Move();
        Console.Read();
    }
}

3. 接口隔离原则-若有关系,语义上要仅依赖自己需要的服务

概括:要求客户端不应该依赖那些它不需要的接口,将一些大的接口细化成一些小的接口供客户端使用
优点:分解成多个小接口,提高代码复用性
缺点:定义过小的接口,造成接口数目过多,使设计复杂化

违反原则例子

class Person
{
    
    
    public void LetAnimalInRefrigerator(string animalName)
    {
    
    
        //函数内容极容易复用
        Console.WriteLine("打开冰箱");
        Console.WriteLine("将" + animalName + "放进冰箱");
        Console.WriteLine("关闭冰箱");
    }
}

符合原则例子

class Person
{
    
    
    public void LetAnimalInRefrigerator(string animalName)
    {
    
    
        OpenRefrigerator();
        Console.WriteLine("将" + animalName + "放进冰箱");
        CloseRefrigerator();
    }

    public void OpenRefrigerator()
    {
    
    
        Console.WriteLine("打开冰箱");
    }

    public void CloseRefrigerator()
    {
    
    
        Console.WriteLine("关闭冰箱");
    }
    
    //新增的方法就可以复用上面两个方法
    public void TakeSomethingInRefrigerator(string thingName)
    {
    
    
        OpenRefrigerator();
        Console.WriteLine("将" + thingName + "拿出");
        CloseRefrigerator();
    }
}

4. 合成复用原则-若要复用,优先使用关联关系

概括:要求复用时尽量使用对象组合,而不使用继承
优点:降低类之间的耦合度,类之间的影响较小
缺点:通过组合/聚合的对象,容易产生过多的对象

违反原则例子

class Person
{
    
    
    public void Move()
    {
    
    
        Console.WriteLine("走...");
    }
}

class Student : Person
{
    
    
    public void GoSchool()
    {
    
    
        Move();
        Console.Write("到学校");
    }
}

class Laborer : Person
{
    
    
    //由于继承导致多出Move方法
    public void GoCompany()
    {
    
    
        Console.Write("跑...到公司");
    }
}

符合原则例子

interface IMoveable
{
    
    
    void Move();
}

class Student : IMoveable
{
    
    
    public void Move()
    {
    
    
        Console.WriteLine("走...到学校");
    }
}

class Laborer : IMoveable
{
    
    
    public void Move()
    {
    
    
        Console.WriteLine("跑...到公司");
    }
}

class Tyler
{
    
    
    public void Move(IMoveable iMoveable)
    {
    
    
        iMoveable.Move();
    }
}

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        //通过接口,使类更加灵活
        IMoveable iMoveable = new Laborer();
        Tyler tyler = new Tyler();
        tyler.Move(iMoveable);

        Console.Read();
    }
}

5. 里氏替换原则-非要使用继承,需满足该原则

概括:可以通俗表述为如果能够使用基类对象,那么一定能够使用其子类对象
优点:子类可以形似基类,但又与基类不同
缺点:子类必须拥有基类对象的全部属性和方法,包括不需要的

违反原则例子

class Animal
{
    
    
    protected string name;
    public virtual void SetName(string name)
    {
    
    
        this.name = name;
    }

    public string GetName()
    {
    
    
        return name;
    }
}

class Bird:Animal
{
    
    
    //子类重写了方法,违背了里氏替换原则,使得调用基类方法与子类方法不一样
    public override void SetName(string name)
    {
    
    
        this.name = name + "鸟";
    }
}

class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        Animal bird1 = new Animal();
        bird1.SetName("布谷");
        Console.WriteLine(bird1.GetName());//布谷

        Animal bird2 = new Bird();
        bird2.SetName("布谷");
        Console.WriteLine(bird2.GetName());//布谷鸟

        Console.Read();
    }
}

符合原则例子

class Animal
{
    
    
    protected string name;
    public void SetName(string name)
    {
    
    
        this.name = name;
    }

    public string GetName()
    {
    
    
        return name;
    }

}
class Bird:Animal
{
    
    
    //与基类方法不同,且拥有基类的方法
    public void SetBirdName(string name)
    {
    
    
        this.name = name + "鸟";
    }
}
class Program
{
    
    
    static void Main(string[] args)
    {
    
    
        Animal bird1 = new Animal();
        bird1.SetName("咕咕");
        Console.WriteLine(bird1.GetName());//咕咕

        Bird bird2 = new Bird();
        bird2.SetBirdName("布谷");
        Console.WriteLine(bird2.GetName());//布谷鸟

        Console.Read();
    }
}

因为作者精力有限,文章中难免出现一些错漏,敬请广大专家和网友批评、指正。

猜你喜欢

转载自blog.csdn.net/qq_46051312/article/details/125747221