面向对象与设计模式第一节:深入理解OOP

第三章:面向对象与设计模式

第一节:深入理解OOP

面向对象编程(OOP)是一种编程范式,它将程序结构视为由对象组成,促进了代码的重用性和可维护性。在这一课中,我们将深入分析OOP的四个基本特性:封装、继承、多态和抽象,并提供相应的示例与实践。

1. OOP基本特性
1.1 封装

封装是OOP的核心概念之一,它指的是将对象的状态(属性)和行为(方法)组合在一起,同时隐藏内部实现细节,只暴露必要的接口。这使得对象可以保护自己的数据不被外部干扰,从而提高了代码的安全性和稳定性。

示例

class BankAccount {
private:double balance; // 账户余额public:BankAccount(double initial_balance) : balance(initial_balance) {}void deposit(double amount) {if (amount > 0) {balance += amount;}}void withdraw(double amount) {if (amount > 0 && amount <= balance) {balance -= amount;}}double getBalance() const {return balance;}
};

在上面的示例中,balance属性被声明为私有(private),外部代码无法直接访问。只有通过公共方法(depositwithdrawgetBalance),才能修改或获取余额。

1.2 继承

继承允许一个类从另一个类派生,从而获得其属性和行为。这种机制支持代码重用和逻辑结构的组织,使得程序设计更加灵活。

示例

class SavingsAccount : public BankAccount {
private:double interestRate; // 利率public:SavingsAccount(double initial_balance, double rate): BankAccount(initial_balance), interestRate(rate) {}void applyInterest() {deposit(getBalance() * interestRate);}
};

在这个例子中,SavingsAccount类继承自BankAccount类,得到了其所有的属性和方法,并增加了一个新的方法applyInterest,用于计算利息。

1.3 多态

多态允许对象以不同的形式表现。这意味着可以使用同一接口来处理不同类型的对象。这通常通过虚函数实现,使得程序可以在运行时选择调用哪个函数。

示例

class Shape {
public:virtual void draw() const = 0; // 纯虚函数
};class Circle : public Shape {
public:void draw() const override {// 画圆的逻辑}
};class Rectangle : public Shape {
public:void draw() const override {// 画矩形的逻辑}
};void render(const Shape& shape) {shape.draw(); // 动态调用
}

在这个示例中,Shape是一个基类,定义了一个纯虚函数drawCircleRectangle类实现了这个函数。通过传递Shape引用,render函数能够根据实际对象类型动态调用相应的draw方法。

1.4 抽象

抽象是指通过提取对象的共同特征来创建类。抽象类通常包含纯虚函数,无法实例化。它们提供了一个模板,其他类可以从中继承并实现特定的功能。

示例

class Animal {
public:virtual void makeSound() const = 0; // 抽象方法
};class Dog : public Animal {
public:void makeSound() const override {// 狗叫的逻辑}
};class Cat : public Animal {
public:void makeSound() const override {// 猫叫的逻辑}
};

在这个例子中,Animal是一个抽象类,定义了一个纯虚函数makeSoundDogCat类实现了这个函数,表示它们各自的叫声。

2. 类的设计原则与实际案例

设计原则帮助开发者编写可维护、可扩展和可重用的代码。以下是几个常用的设计原则:

2.1 单一职责原则(SRP)

一个类应该只有一个单一的职责,所有的功能都应该围绕这个职责展开。这可以减少类的复杂性,便于维护和修改。

示例

class User {
public:void login() {// 登录逻辑}
};class UserNotifier {
public:void notifyUser() {// 通知用户逻辑}
};

在这个示例中,User类负责用户登录,而UserNotifier类负责用户通知。两个类各司其职,符合单一职责原则。

2.2 开放封闭原则(OCP)

软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着可以通过添加新代码而不是修改现有代码来实现新功能。

示例

class Shape {
public:virtual double area() const = 0; // 纯虚函数
};class Circle : public Shape {
public:double area() const override {// 计算圆的面积}
};class Rectangle : public Shape {
public:double area() const override {// 计算矩形的面积}
};// 新增多边形类
class Polygon : public Shape {
public:double area() const override {// 计算多边形的面积}
};

在这个例子中,新增的Polygon类并没有修改现有的代码,而是通过继承Shape类实现新的功能,符合开放封闭原则。

2.3 里氏替换原则(LSP)

子类对象应该能够替换父类对象,而不会影响程序的正确性。这意味着子类必须符合父类的约定。

示例

void drawShape(const Shape& shape) {shape.draw(); // 可以接受任何形状的子类
}

确保CircleRectangle等子类都能正确执行父类的方法,不会引入错误。

2.4 接口隔离原则(ISP)

不应强迫客户依赖于他们不使用的接口。接口应该细化为特定的、功能单一的接口。

示例

class IShape {
public:virtual void draw() const = 0;
};class IColor {
public:virtual void fill() const = 0;
};class Circle : public IShape, public IColor {
public:void draw() const override {// 画圆的逻辑}void fill() const override {// 填充圆的逻辑}
};

在这个例子中,IShapeIColor接口分别定义了不同的功能,避免了客户不必要的依赖。

2.5 依赖倒置原则(DIP)

高层模块不应该依赖低层模块,二者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。

示例

class Notification {
public:virtual void send() = 0; // 抽象通知
};class EmailNotification : public Notification {
public:void send() override {// 发送电子邮件逻辑}
};class SmsNotification : public Notification {
public:void send() override {// 发送短信逻辑}
};class User {Notification* notifier;public:User(Notification* n) : notifier(n) {}void notify() {notifier->send(); // 使用抽象发送通知}
};

在这个示例中,User类依赖于Notification接口而不是具体的通知实现,从而实现了依赖倒置原则。

3. 项目实践

在实际项目中,运用OOP的特性和设计原则是非常重要的。下面是一个基于OOP的简单项目示例。

3.1 项目背景

我们要创建一个简单的图形编辑器,能够支持绘制多种形状并计算其面积。通过OOP的特性,我们可以设计出灵活且易于扩展的结构。

3.2 类的设计
  • Shape类作为抽象基类,定义绘制和计算面积的方法。
  • 具体的形状类(如CircleRectangle)继承自Shape并实现相关方法。
  • ShapeManager类负责管理所有形状,提供添加、删除和遍历功能。
3.3 代码示例
#include <iostream>
#include <vector>
#include <memory>class Shape {
public:virtual void draw() const = 0;virtual double area() const = 0;virtual ~Shape() = default; // 虚析构函数
};class Circle : public Shape {
private:double radius;public:Circle(double r) : radius(r) {}void draw() const override {std::cout << "Drawing Circle with radius: " << radius << std::endl;}double area() const override {return 3.14 * radius * radius;}
};class Rectangle : public Shape {
private:double width, height;public:Rectangle(double w, double h) : width(w), height(h) {}void draw() const override {std::cout << "Drawing Rectangle with width: " << width << " and height: " << height << std::endl;}double area() const override {return width * height;}
};class ShapeManager {
private:std::vector<std::shared_ptr<Shape>> shapes;public:void addShape(std::shared_ptr<Shape> shape) {shapes.push_back(shape);}void drawAll() const {for (const auto& shape : shapes) {shape->draw();}}void calculateTotalArea() const {double totalArea = 0;for (const auto& shape : shapes) {totalArea += shape->area();}std::cout << "Total Area: " << totalArea << std::endl;}
};int main() {ShapeManager manager;manager.addShape(std::make_shared<Circle>(5.0));manager.addShape(std::make_shared<Rectangle>(4.0, 6.0));manager.drawAll();manager.calculateTotalArea();return 0;
}

在这个简单的图形编辑器项目中,我们通过OOP的特性和设计原则,实现了一个可扩展的框架。将来可以很方便地添加新的形状类,而无需修改现有代码。

总结

本课深入探讨了面向对象编程的基本特性,包括封装、继承、多态和抽象,以及如何应用这些特性设计出符合OOP原则的类。在实际开发中,遵循设计原则有助于编写可维护、可扩展的代码。通过简单的项目实例,我们展示了OOP在实际中的应用,帮助初级到中级程序员更好地理解和应用OOP的理念。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[JAVAEE] 多线程的案例(三) - 线程池

目录 一. 什么是线程池 二. 线程池的作用 三. java提供的线程池类 四. ThreadPoolExecutor的构造方法及参数理解 1. int corePoolSize: 核心线程数. 2. int maximumPoolSize: 最大线程数 核心线程数 非核心线程数 3. int keepAliveTime:非核心线程允许空闲的最大时间. …

DataX简介及使用

目录 一、DataX离线同步工具DataX3.0介绍 1.1、 DataX 3.0概览 1.2、特征 1.3、DataX3.0框架设计 1.4、支持的数据元 1.5、DataX3.0核心架构 1.6、DataX 3.0六大核心优势 1.6.1、可靠的数据质量监控 1.6.2、丰富的数据转换功能 1.6.3、精准的速度控制 1.6.4、强劲的…

正则表达式和通配符

文章目录 正则表达式和通配符的区别正则表达式&#xff08;Regex&#xff09;通配符&#xff08;Wildcards&#xff09;总结 正则表达式的概念正则表达式的由来为什么要使用正则表达式 正则表达式的语法组成修饰符元字符\f\b\B 在Linux中的基础正则和扩展正则基础正则(BRE)^$.*…

面试时被问到“Scaling Law”,该怎么答?

在大模型的研发中&#xff0c;通常会有下面一些需求&#xff1a; 计划训练一个 10B 的模型&#xff0c;想知道至少需要多大的数据&#xff1f; 收集到了 1T 的数据&#xff0c;想知道能训练一个多大的模型&#xff1f; 老板准备 1 个月后开发布会&#xff0c;给的资源是 100 …

Linux安装Nginx教程(rpm安装方式)

本章教程,主要介绍如何在Linux Centos7系统上,使用rpm的方式进行安装Nginx。 一、安装wget插件 如果不存在wget下载插件,需要安装一下。 yum install -y wget二 、下载rpm安装包 官方提供的rpm下载地址:https://nginx.org/packages/centos/7/x86_64/RPMS/ <

【Nginx系列】499错误

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Postman常见问题及解决方(全)

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、网络连接问题 如果Postman无法发送请求或接收响应&#xff0c;可以尝试以下操作&#xff1a; 检查网络连接是否正常&#xff0c;包括检查网络设置、代理设置…

软考中级嵌入式系统设计师笔记分享(二)

1.TTL 电路是电流控制器件&#xff0c;而CMOS 电路是电压控制器件。 2.TTL 电路的速度快&#xff0c;传输延迟时间短(5-10ns)&#xff0c;但是功耗大。 常见的串行总线有 SPI、II2C、USB、RS232/RS422/RS485、CAN等;高速串行总线主要有 SATA、PCIE、IEEE 1394、Rapidl0、USB 3…

1.DBeaver连接hive数据库

1.hive开启远程服务&#xff0c;linux中直接输入&#xff1a;hiveserver2 2.解压dbeaver和hive-jdbc-2.1.1.zip 3.双击打开 4.数据库&#xff0c;新建连接 5.搜索hive 6.配置参数 7.编辑驱动设置 8.添加jar包 9.测试连接 10.右击&#xff0c;新建sql编辑器 11.执行sql 12.调整字…

【每日一题】LeetCode - 整数转罗马数字

在罗马数字系统中&#xff0c;七个不同的符号代表不同的值&#xff1a; 符号值I1V5X10L50C100D500M1000 罗马数字的表示方式是从最大值开始逐次减去每个符号的值&#xff0c;通过组合这些符号构建最终的表示形式。本文将介绍一个基于贪心策略的解决方案&#xff0c;将整数转换…

unity开发之Line Renderer

Line Renderer 是一个有用的工具&#xff0c;可让您在游戏中绘制线条。 它可以用作游戏的函数或调试标记。 在这里&#xff0c;让我们创建一个程序&#xff0c;根据基本用法在 Line Renderer 上移动。 目录 如何使用 Line Renderer 和基础知识 在场景中放置 Line Renderer关键组…

Catalan数 C++解决

输入描述 输入一个正整数n。 输出描述 输出Catalan数的前n项。 用例输入 1 0 用例输出 1 1 用例输入 2 5 用例输出 2 1 1 2 5 14 42 #include<bits/stdc.h> using namespace std; int main() {int n;cin>>n;int dp[n1]{0};dp[0]dp[1]1;for(int m2;…

守护头顶安全——AI高空抛物监测,让悲剧不再重演

在城市的喧嚣中&#xff0c;我们享受着高楼林立带来的便捷与繁华&#xff0c;却往往忽视了那些隐藏在高空中的危险。近日&#xff0c;震惊全国的高空抛物死刑案件被最高院核准并执行。案件中被告人多次高空抛物的举动&#xff0c;夺去了无辜者的生命&#xff0c;也让自己付出了…

Go 语言中的 for range 循环教程

在 Go 语言中&#xff0c;for range 循环是一个方便的语法结构&#xff0c;用于遍历数组、切片、映射和字符串。本教程将通过示例代码来帮助理解如何在 Go 中使用 for range 循环。 package mainimport "fmt"func main() {// 遍历切片并计算和nums : []int{2, 3, 4}…

Kafka-代码示例

一、构建开发环境 File > New > Project 选择一个最简单的模板 项目和坐标命名 配置maven路径 添加maven依赖 <dependencies><!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients --><dependency><groupId>org.apache.kaf…

深度学习 基本函数01

np.dot 是 NumPy 库中的一个函数&#xff0c;用于计算两个数组的点积&#xff08;也称为内积或数量积&#xff09;。点积是两个向量的对应元素乘积之和。 np.random.normal 是 NumPy 库中的一个函数&#xff0c;用于生成符合正态分布&#xff08;也称为高斯分布&#xff09;的…

项目管理软件中这6个小技巧帮助项目经理同时管理多个项目

在网上看到一个数据&#xff0c;只有15%的项目经理一次只需要负责一个项目&#xff0c;其他的项目经理都需要同时负责多个项目&#xff0c;甚至有15%的项目经理一次需要负责10个以上的项目。 我在工作中&#xff0c;也只有很少很少的时间里&#xff0c;是一次性只负责一个项目…

目标检测——yolov5-3.1的环境搭建和运行

第一步&#xff1a;安装anaconda环境&#xff0c;并且配置好cuda&#xff0c;安装需要的基本包 查看对应cuda版本&#xff0c;后续下载cudatoolkit需要对应版本 nvcc -V 第二步&#xff1a;创建虚拟环境&#xff0c;激活环境&#xff0c;安装所需的包 conda create -n yolo…

Visual studio 下载安装

1&#xff0c;Visual stutdio 网址 下载 Visual Studio Tools - 免费安装 Windows、Mac、Linux 2&#xff0c;下划页面&#xff0c;点击 较早的下载 3&#xff0c;选择对应的版本进行下载

《深度学习》YOLO v1网络架构 、损失值、NMS极大值抑制

目录 一、Yolo系列v1 1、核心思想 2、示例 3、流程图解析 二、YOLO系列v1损失函数 1、位置误差 2、置信度误差 3、类别概率损失 三、NMS非极大值抑制 1、概念 2、步骤 四、YOLO v1优缺点 1、优点 1&#xff09;速度快 2&#xff09;端到端 3&#xff09;多尺度…