C#探索之路基础夯实篇(3):面向对象的三大特性和五大原则详解

文章目录

      • 前提:
      • 一、特性:
      • 二、原则:
      • 三、示例
        • 1. 单一职责原则 (Single Responsibility Principle, SRP):
        • 2. 开放-封闭原则 (Open-Closed Principle, OCP):
        • 3. 里氏替换原则 (Liskov Substitution Principle, LSP):
        • 4. 接口隔离原则 (Interface Segregation Principle, ISP):
        • 5. 依赖倒置原则 (Dependency Inversion Principle, DIP):

前提:

在维护旧功能和扩展功能的时候,我时常发现一个维护代码代价的痛点,尤其是上一个写当前功能的人不是我的时候,维护起来就需要看到别人代码的扩展性是否足够高,是否便于进行二次开发。这个就让我想起来了编程原则。
面向对象编程(Object-Oriented Programming, OOP)的原则和特性如下所示:

一、特性:

  1. 类与对象

    • 类是对象的抽象描述,它定义了对象的属性和行为。对象是类的实例,具体化了类的属性和行为。
  2. 方法(Method)

    • 方法是类中的函数,用于执行特定的操作。方法通常用于操作对象的状态或提供对象的行为。
  3. 继承与派生

    • 继承允许新类(子类)基于现有类(父类)的定义来创建。子类可以继承父类的属性和方法,并且可以添加新的属性和方法。
  4. 多态性

    • 多态性允许不同类的对象对同一消息作出不同的响应。这提高了代码的灵活性和可维护性。
  5. 抽象(Abstraction)

    • 抽象是指将复杂的现实世界问题简化为程序设计中的对象模型。通过抽象,程序员可以专注于对象的关键特征,并忽略不相关的细节。
  6. 封装性

    • 封装性通过将数据和方法组合成一个单元,并限制对数据的直接访问,保护了对象的状态。这样可以防止意外修改对象的状态,提高了代码的可靠性和安全性。
  7. 消息传递(Message Passing)

    • 对象之间的通信是通过发送消息来实现的。对象通过调用其他对象的方法来发送消息,从而实现相互之间的交互和协作。

    其中我们所提及的三大特性主要指的是:

    1. 封装(Encapsulation)

      • 封装将数据和行为组合成一个单一的单元,并将其限制在类的内部。对象的内部状态只能通过公共接口访问,而不是直接暴露给外部。
    2. 继承(Inheritance)

      • 继承允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以使用父类的属性和方法,并且可以添加自己的特定功能。
    3. 多态(Polymorphism)

      • 多态允许对象在运行时表现出不同的行为。即使是相同的方法调用,具体执行的操作也可能因对象的类型而异。这提高了代码的灵活性和可重用性。

这些原则和特性共同构成了面向对象编程范式的核心。通过遵循这些原则和特性,开发人员可以编写出结构清晰、可维护、可扩展的代码。

二、原则:

在面向对象编程中,编写的代码应该符合以下原则:

  1. 单一职责原则(Single Responsibility Principle, SRP)

    • 一个类应该只有一个引起变化的原因。换句话说,一个类应该只负责一项任务。这样可以提高代码的内聚性,降低类的复杂度,使代码更容易理解和维护。
  2. 开放-封闭原则(Open-Closed Principle, OCP)

    • 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码的情况下,应该能够通过扩展来添加新的功能。通过使用抽象类、接口和多态等技术,可以实现对修改关闭的设计。
  3. 里氏替换原则(Liskov Substitution Principle, LSP)

    • 所有引用基类(父类)的地方必须能够透明地使用其子类的对象,而不会影响程序的正确性。这意味着子类必须能够完全替代其父类,而不引起意外行为。
  4. 接口隔离原则(Interface Segregation Principle, ISP)

    • 不应该强迫客户端依赖于它们不使用的接口。接口应该尽可能小,只包含客户端需要的方法。这样可以避免不必要的依赖,并使系统更加灵活和易于维护。
  5. 依赖倒置原则(Dependency Inversion Principle, DIP)

    • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。通过依赖注入和依赖倒置容器等技术,可以实现低耦合和高内聚的设计。

遵循这些原则可以帮助开发人员编写出高质量、可维护、可扩展的面向对象代码,提高代码的质量和可靠性。

三、示例

以下是针对每种原则的具体C#代码示例:

1. 单一职责原则 (Single Responsibility Principle, SRP):
using System;

// Violating SRP 违背单一职责原则,类的职责不再单一
class Car {
    public void StartEngine() {
        Console.WriteLine("Engine started");
    }
    
    public void Drive() {
        Console.WriteLine("Car is driving");
    }
    
    public void PlayMusic() {
        Console.WriteLine("Playing music");
    }
}

// Following SRP 遵守单一职责的原则,类的职责依旧单一
class Car {
    public void StartEngine() {
        Console.WriteLine("Engine started");
    }
    
    public void Drive() {
        Console.WriteLine("Car is driving");
    }
}

class MusicPlayer {
    public void PlayMusic() {
        Console.WriteLine("Playing music");
    }
}

// Client code
class Program {
    static void Main(string[] args) {
        Car car = new Car();
        car.StartEngine();
        car.Drive();
        
        MusicPlayer player = new MusicPlayer();
        player.PlayMusic();
    }
}

在这个例子中,Car 类负责汽车的启动和驾驶,而 MusicPlayer 类负责音乐播放。这样,每个类都只有一个单一的职责。

2. 开放-封闭原则 (Open-Closed Principle, OCP):
using System;

// Violating OCP 违背了开放-封闭原则
class Shape {
    public string Type { get; set; }
    public void Draw() {
        if (Type == "Circle") {
            Console.WriteLine("Drawing circle");
        } else if (Type == "Rectangle") {
            Console.WriteLine("Drawing rectangle");
        }
    }
}

// Following OCP 遵守开放-封闭原则
abstract class Shape {
    public abstract void Draw();
}

class Circle : Shape {
    public override void Draw() {
        Console.WriteLine("Drawing circle");
    }
}

class Rectangle : Shape {
    public override void Draw() {
        Console.WriteLine("Drawing rectangle");
    }
}

// Client code
class Program {
    static void Main(string[] args) {
        Shape circle = new Circle();
        circle.Draw();
        
        Shape rectangle = new Rectangle();
        rectangle.Draw();
    }
}

在这个例子中,我们定义了一个抽象的 Shape 类,以及具体的子类 CircleRectangle。通过这种方式,我们可以通过添加新的形状类来扩展系统,而不需要修改现有的代码。

3. 里氏替换原则 (Liskov Substitution Principle, LSP):
using System;

// Violating LSP 违背里氏替换原则
class Rectangle {
    public virtual int Width { get; set; }
    public virtual int Height { get; set; }

    public int CalculateArea() {
        return Width * Height;
    }
}

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

// Following LSP  遵守里氏替换原则
abstract class Shape {
    public abstract int CalculateArea();
}

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

    public override int CalculateArea() {
        return Width * Height;
    }
}

class Square : Shape {
    public int SideLength { get; set; }

    public override int CalculateArea() {
        return SideLength * SideLength;
    }
}

// Client code
class Program {
    static void Main(string[] args) {
        Shape rectangle = new Rectangle { Width = 3, Height = 4 };
        Console.WriteLine("Rectangle area: " + rectangle.CalculateArea());
        
        Shape square = new Square { SideLength = 5 };
        Console.WriteLine("Square area: " + square.CalculateArea());
    }
}

在这个例子中,Square 类不再是 Rectangle 类的子类,因为它改变了父类中的行为。通过将 RectangleSquare 都实现为 Shape 的子类,我们遵循了里氏替换原则。

4. 接口隔离原则 (Interface Segregation Principle, ISP):
using System;

// Violating ISP 违背了接口隔离原则,使得接口的含义不明确
interface IWorker {
    void Work();
    void TakeBreak();
    void ClockIn();
    void ClockOut();
}

class Worker : IWorker {
    public void Work() {
        Console.WriteLine("Working");
    }
    
    public void TakeBreak() {
        Console.WriteLine("Taking a break");
    }
    
    public void ClockIn() {
        Console.WriteLine("Clocking in");
    }
    
    public void ClockOut() {
        Console.WriteLine("Clocking out");
    }
}

// Following ISP 遵守了接口隔离原则
interface IWorker {
    void Work();
}

interface IBreak {
    void TakeBreak();
}

interface ITimeClock {
    void ClockIn();
    void ClockOut();
}

class Worker : IWorker, IBreak, ITimeClock {
    public void Work() {
        Console.WriteLine("Working");
    }
    
    public void TakeBreak() {
        Console.WriteLine("Taking a break");
    }
    
    public void ClockIn() {
        Console.WriteLine("Clocking in");
    }
    
    public void ClockOut() {
        Console.WriteLine("Clocking out");
    }
}

// Client code
class Program {
    static void Main(string[] args) {
        Worker worker = new Worker();
        worker.Work();
        worker.TakeBreak();
        worker.ClockIn();
        worker.ClockOut();
    }
}

在这个例子中,我们将 IWorker 接口拆分为 IWorkerIBreakITimeClock 接口,以更好地符合接口隔离原则。每个接口都代表一个独立的功能领域,使得类只需要实现其相关的接口。

5. 依赖倒置原则 (Dependency Inversion Principle, DIP):
using System;

// Violating DIP 
class LightBulb {
    public void TurnOn() {
        Console.WriteLine("Light bulb turned on");
    }
}

class LightSwitch {
    private LightBulb bulb = new LightBulb();

    public void Flip() {
        bulb.TurnOn();
    }
}

// Following DIP
interface ISwitchable {
    void TurnOn();
}

class LightBulb : ISwitchable {
    public void TurnOn() {
        Console.WriteLine("Light bulb turned on");
    }
}

class Fan : ISwitchable {
    public void TurnOn() {
        Console.WriteLine("Fan turned on");
    }
}

class Switch {
    private ISwitchable device;

    public Switch(ISwitchable device) {
        this.device = device;
    }

    public void Flip() {
        device.TurnOn();
    }


}

// Client code
class Program {
    static void Main(string[] args) {
        ISwitchable bulb = new LightBulb();
        Switch lightSwitch = new Switch(bulb);
        lightSwitch.Flip();
        
        ISwitchable fan = new Fan();
        Switch fanSwitch = new Switch(fan);
        fanSwitch.Flip();
    }
}

在这个例子中,Switch 类不再直接依赖于 LightBulb 类,而是依赖于 ISwitchable 接口。这样,我们可以在不修改 Switch 类的情况下轻松地将其用于控制其他可开关的设备,符合依赖倒置原则。

这些示例演示了如何在C#中应用面向对象编程原则。通过遵循这些原则,可以编写出更加模块化、灵活和可维护的代码。

在这里插入图片描述

公众号:平平无奇代码猴
也可以搜索:Jackiie_wang 公众号,欢迎大家关注!欢迎催更!留言!

作者:ProMer_Wang

链接:https://blog.csdn.net/qq_43801020/article/details/137443640

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/521833.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

51单片机实验03-定时器T0来实现流水灯从左到右再从右到左

目录 一、实验目的 二、实验说明 1、51单片机有两个16位内部计数器/定时器(C/T, Counter/Timer)。 2、模式寄存器TMOD 1) M1M0工作模式控制位; 2) C/T定时器或计数器选择位: 3)GATE定时器/计数器运行…

高可用集群-keepalived

一、高可用集群的基本理论 1.基本概念 ①:单点故障(Single Point of Failure) 硬件故障:设计缺陷、wear out(损耗)、自然灾害…… 软件故障:设计缺陷 bug ②:健康状态监测 监测手段…

使用 RisingWave、NATS JetStream 和 Superset 进行实时物联网监控

在物联网(IoT)背景下,处理实时数据会遇到一些特定的障碍,如边缘计算资源不足、网络条件限制、扩展性存在问题、设备间有多样性差异。要克服这些挑战,需要高效的边缘计算技术、强大的安全措施、标准化协议、可扩展的管理…

Pytorch数据结构:GPU加速

文章目录 一、GPU加速1. 检查GPU可用性:2. GPU不可用需要具体查看问题3. 指定设备4.将张量和模型转移到GPU5.执行计算:6.将结果转移回CPU 二、转移原理1. 数据和模型的存储2. 数据传输3. 计算执行4. 设备管理5.小结 三、to方法的参数类型 一、GPU加速 .…

html写一个登录注册页面

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册登录界面Ⅰ</title><link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.mi…

GA-SVM,基于GA遗传算法优化SVM支持向量机回归预测(多输入单输出)

基于遗传算法&#xff08;Genetic Algorithm, GA&#xff09;优化支持向量机&#xff08;Support Vector Machine, SVM&#xff09;用于回归预测是一个常见的任务。在这个任务中&#xff0c;我们使用GA来寻找SVM的最佳超参数配置&#xff0c;以最大化回归性能指标&#xff0c;例…

力控机器人原理及力控制实现

力控机器人原理及力控制实现 力控机器人是一种能够感知力量并具有实时控制能力的机器人系统。它们可以在与人类进行精准协作和合作时&#xff0c;将力传感技术&#xff08;Force Sensing Technology&#xff09;和控制算法&#xff08;Control Algorithm&#xff09;结合起来&a…

C++STL--排序算法

sort 使用快速排序,平均性能好O(nlogn),但最差情况可能很差O(n^2)。不稳定。 sort(v.begin(),v.end());//对v容器进行排序,默认升序 sort(v.begin(),v.end(),greater<int>());//降序排序对于支持随机访问的迭代器的容器&#xff0c; 都可以利用sort算法直接对其进行排序…

克罗地亚公司注册

克罗地亚是一个发达的资本主义国家&#xff0c;经济基础良好&#xff0c;旅游建筑造船和制药等产业发展水平较高&#xff0c;优质的基础设施&#xff0c;低成本的多语种和高技能劳动力&#xff0c;有力的地理环境&#xff0c;使旅游业发展充满活力&#xff0c;克罗地亚政府承诺…

vue+elementUI实现表格组件的封装

效果图&#xff1a; 在父组件使用表格组件 <table-listref"table":stripe"true":loading"loading":set-table-h"slotProps.setMainCardBodyH":table-data"tableData":columns"columns.tableList || []":ra…

重磅!天途推出平安校园管理平台

天途平安校园管理平台&#xff0c;是围绕校园安全事件开发的一款智能监控与巡逻系统。系统通过大疆机场、无人机和校园内的监控硬件等多端传输的视频和图片信息&#xff0c;经过 AI分析处理后形成告警信息并及时通知学校安保人员。 天途平安校园管理平台 平安校园管理平台优势 …

【深度学习】从基础原理到未来挑战的全面探索

深度学习的基本原理 深度学习&#xff0c;一种模拟人脑分析和处理数据的机器学习技术&#xff0c;已成为人工智能研究中最令人兴奋的进展之一。其核心在于构建和训练神经网络&#xff0c;这些网络由多个层次组成&#xff0c;每一层都能从输入数据中提取并转换特征。随着数据层层…

[Java线程池]ExecutorService|CompletionService的区别与选择

这段时间对业务系统做了个性能测试&#xff0c;其中使用了较多线程池的技术&#xff0c;故此做一个技术总结。 这次总结的内容比较多&#xff0c;主要是四个&#xff1a; ExecutorServiceCompletionServiceRunnableCallable 前两个是线程池相关接口&#xff0c;后两个是多线…

《手把手教你》系列基础篇(七十四)-java+ selenium自动化测试-框架设计基础-TestNG实现DDT - 上篇(详解教程)

1.简介 上一篇文章中宏哥简单的讲解了一下通过xml文件传递参数&#xff0c;这一篇宏哥讲解通过通过DataProvider传递参数&#xff0c;也就是我们常说的数据驱动测试。如何利用TestNG实现DDT&#xff08;数据驱动测试 Data Driver Test&#xff09;&#xff0c;什么是数据驱动测…

椋鸟数据结构笔记#8:二叉树的遍历、创建与销毁

萌新的学习笔记&#xff0c;写错了恳请斧正。 链式二叉树 这篇笔记我们讨论基于链式二叉树&#xff0c;其节点的数据结构如下&#xff1a; typedef int BTDatatype;typedef struct BTNode {BTDataType data;struct BTNode* left;struct BTNode* right; } BTNode;二叉树的遍历…

STM32CubeMX配置步骤详解六 —— 时钟及其它内部参数配置(1)

接前一篇文章&#xff1a;STM32CubeMX配置步骤详解五 —— 基础配置&#xff08;2&#xff09; 本文内容主要参考&#xff1a; STM32CUBEMX配置教程&#xff08;一&#xff09;基础配置-CSDN博客 野火STM32系列HAL库开发教程 —— 第12讲 STM32的复位和时钟控制&#xff08;第…

环形链表 - LeetCode 热题 25

大家好&#xff01;我是曾续缘&#x1f970; 今天是《LeetCode 热题 100》系列 发车第 25 天 链表第 4 题 ❤️点赞 &#x1f44d; 收藏 ⭐再看&#xff0c;养成习惯 环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可…

2-3 AUTOSAR ASW Runable可运行实体

返回总目录->返回总目录<- 目录 一、概述 二、RTE Event 一、概述 运行实体(Runnable Entity,RE)是一段可执行的代码,其包含实际实现的函数(具体的逻辑算法或者操作)。一个软件组件可以包含一个或者多个运行实体。 Runnable就是SWC中的函数,而在AutoSAR架构在被…

【云计算】云数据中心网络(一):VPC

云数据中心网络&#xff08;一&#xff09;&#xff1a;VPC 1.什么是 VPC2.VPC 的组成2.1 虚拟交换机2.2 虚拟路由器 3.VPC 网络规划3.1 VPC 数量规划3.2 交换机数量规划3.3 地址空间规划3.4 不同规模企业地址空间规划实践 4.VPC 网络高可靠设计4.1 单地域单可用区部署4.2 单地…