面向对象软件编程——OOP入门实践

news2024/9/22 7:25:36

工作一段时间了,工作内容趋向于算法模型的复现,就是复现论文算法然后结合业务逻辑开发软件。但是在设计和开发软件时,发现对于OOP理念和软件的设计原则等在实战中还是非常缺乏。于是开始补习,基础软件开发技术。

书籍:《深入浅出WPF》

作者:刘铁猛

序:WPF(Windows Presentation Foundation)编写程序表示层的技术与工具。

程序架构: 表示层(View)<-->业务逻辑层(ViewModel)<-->数据层(Model)。也就是一般常说的MVVM

数据模型:现实世界中事物和逻辑的抽象。

业务逻辑:数据模型之间的关系与交互。

用户界面:由控件构成的、与用户进行交互的界面,用于把数据展示给用户并响应用户的输入。

好,序结束了,本书可以暂告一段落了。先插入OOP,再来WPF的具体开发。

OOP(Object-Oriented Programming)实践:(以下代码示例由ChatGPT给出)

目录

1. 对象组合

1.1 一对一对象组合:两种典型的对象组合方式

1.1.1 方式一: 完全包容

1.1.2 方式二:独立方式

1.2 一对多

​编辑

1.2.1 方式一:完全包含

1.2.2 方式二:独立方式

1.3 延时动态对象组合

1.4 对象组合的特殊形式——自引用类

示例:链表中的自引用

自引用类的应用场景:

自引用类的特点:


1. 对象组合

假设我们有一个 Engine 类和一个 Wheels 类,通过组合它们,我们可以创建一个 Car 类,而不必通过继承的方式来实现。

class Engine:
    def start(self):
        print("Engine started")

class Wheels:
    def roll(self):
        print("Wheels rolling")

class Car:
    def __init__(self, engine, wheels):
        self.engine = engine
        self.wheels = wheels

    def drive(self):
        self.engine.start()
        self.wheels.roll()

# 使用对象组合
engine = Engine()
wheels = Wheels()
car = Car(engine, wheels)
car.drive()

1.1 一对一对象组合:两种典型的对象组合方式

1.1.1 方式一: 完全包容

在面向对象编程中,如果对象 A 完全包容对象 B,并且容器对象 A 管理被包容对象 B 的生命周期,这种关系通常被称为 组合(Composition)

下面是一个使用 C# 语言的例子,

  • Engine 类 定义了引擎的启动和停止行为。

  • Car 类 内部包含一个 Engine 对象,并在 Car 对象的构造函数中创建该对象。

  • Car 类 通过 DriveStop 方法来操作其内部的 Engine 对象。

  • Car 对象被销毁时,Engine 对象也随之被销毁。这种关系说明 Car 对象完全控制了 Engine 对象的生命周期。

在这个例子中,Car 对象是容器对象,它负责创建和销毁 Engine 对象,展示了组合关系中的生命周期管理。

using System;

class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine started");
    }

    public void Stop()
    {
        Console.WriteLine("Engine stopped");
    }
}

class Car
{
    private Engine _engine;

    public Car()
    {
        _engine = new Engine();  // Car对象创建时,同时创建Engine对象
    }

    public void Drive()
    {
        _engine.Start();
        Console.WriteLine("Car is driving...");
    }

    public void Stop()
    {
        _engine.Stop();
        Console.WriteLine("Car has stopped.");
    }

    ~Car()
    {
        // 在Car对象销毁时,同时销毁Engine对象
        Console.WriteLine("Car is being destroyed, so is the engine.");
        _engine = null;
    }
}

class Program
{
    static void Main()
    {
        Car car = new Car();
        car.Drive();
        car.Stop();

        // Car对象出作用域时,垃圾回收器将最终销毁Car对象和它包含的Engine对象
    }
}

1.1.2 方式二:独立方式

在面向对象编程中,如果对象 B 是独立的,并且对象 A 只是引用一个现成的 B 对象,而不负责管理 B 对象的生命周期,这种关系通常被称为 聚合(Aggregation)。在聚合关系中,被引用的对象可以被多个其他对象共享,它的生命周期独立于引用它的对象。

  • Engine 类 定义了引擎的启动和停止行为,与前面的例子相同。

  • Car 类 在其构造函数中接受一个 Engine 对象的引用,但并不创建或销毁该对象。

  • Main 方法中,sharedEngine 是一个独立的 Engine 对象,它的生命周期独立于任何 Car 对象。

  • Car1 和 Car2 都引用同一个 sharedEngine 对象,这表明它们之间存在聚合关系。

在这个例子中,Car 对象和 Engine 对象之间的关系是聚合关系,因为 Car 对象只引用现成的 Engine 对象,而不管理它的生命周期。

using System;

class Engine
{
    public void Start()
    {
        Console.WriteLine("Engine started");
    }

    public void Stop()
    {
        Console.WriteLine("Engine stopped");
    }
}

class Car
{
    private Engine _engine;

    // 构造函数接受一个现成的 Engine 对象
    public Car(Engine engine)
    {
        _engine = engine;
    }

    public void Drive()
    {
        _engine.Start();
        Console.WriteLine("Car is driving...");
    }

    public void Stop()
    {
        _engine.Stop();
        Console.WriteLine("Car has stopped.");
    }
}

class Program
{
    static void Main()
    {
        Engine sharedEngine = new Engine();  // 创建一个独立的 Engine 对象

        Car car1 = new Car(sharedEngine);  // 引用现成的 Engine 对象
        Car car2 = new Car(sharedEngine);  // 另一个 Car 对象也引用同一个 Engine 对象

        car1.Drive();
        car1.Stop();

        car2.Drive();
        car2.Stop();

        // sharedEngine 对象的生命周期独立于 car1 和 car2
    }
}

1.2 一对多

1.2.1 方式一:完全包含

一对多完全包容的特点:

  • 生命周期管理:容器对象(如 Team)负责管理所有子对象(如 Player)的生命周期。一旦容器对象销毁,所有子对象也会被销毁。

  • 封装性:所有子对象都被容器对象内部管理,外部不需要直接处理这些子对象。

  • 统一管理:通过使用集合类型(如 List<Player>),可以方便地对多个子对象进行统一管理和操作。

一对多组合关系的例子,其中一个 Team 对象完全包容并管理多个 Player 对象:

using System;
using System.Collections.Generic;

class Player
{
    public string Name { get; }

    public Player(string name)
    {
        Name = name;
    }

    public void Play()
    {
        Console.WriteLine($"{Name} is playing.");
    }
}

class Team
{
    private List<Player> _players;

    public Team()
    {
        _players = new List<Player>();  // 初始化列表以管理多个Player对象
    }

    public void AddPlayer(string playerName)
    {
        Player player = new Player(playerName);  // 创建并添加新Player对象
        _players.Add(player);
    }

    public void PlayGame()
    {
        Console.WriteLine("The team is playing the game:");
        foreach (var player in _players)
        {
            player.Play();
        }
    }

    ~Team()
    {
        // 在Team对象销毁时,Player对象会随之销毁
        Console.WriteLine("Team is being destroyed, so are the players.");
        _players.Clear();
    }
}

class Program
{
    static void Main()
    {
        Team team = new Team();
        team.AddPlayer("Alice");
        team.AddPlayer("Bob");
        team.AddPlayer("Charlie");

        team.PlayGame();

        // 当 team 对象出作用域并被垃圾回收时,Player 对象也会被销毁
    }
}

1.2.2 方式二:独立方式

一对多独立关系的特点:

  • 独立的生命周期Student 对象的生命周期独立于 School 对象,即使 School 对象被销毁,Student 对象依然可以存在并被其他对象引用。

  • 共享性:多个 School 对象可以共享同一个 Student 对象,或者 Student 对象可以被其他地方使用,而不仅仅是被某个特定的 School 对象引用。

  • 低耦合性School 对象和 Student 对象之间的关系是低耦合的,School 仅仅是引用和使用 Student,而不负责其生命周期管理。

一对多聚合关系的例子,其中一个 School 对象引用多个独立的 Student 对象:

using System;
using System.Collections.Generic;

class Student
{
    public string Name { get; }

    public Student(string name)
    {
        Name = name;
    }

    public void Study()
    {
        Console.WriteLine($"{Name} is studying.");
    }
}

class School
{
    private List<Student> _students;

    public School()
    {
        _students = new List<Student>();  // 初始化列表用于引用多个Student对象
    }

    public void AddStudent(Student student)
    {
        _students.Add(student);  // 将独立的Student对象添加到School中
    }

    public void StartClasses()
    {
        Console.WriteLine("School is starting classes:");
        foreach (var student in _students)
        {
            student.Study();
        }
    }
}

class Program
{
    static void Main()
    {
        // 创建独立的Student对象
        Student student1 = new Student("Alice");
        Student student2 = new Student("Bob");
        Student student3 = new Student("Charlie");

        // 创建School对象
        School school = new School();

        // 将Student对象添加到School中
        school.AddStudent(student1);
        school.AddStudent(student2);
        school.AddStudent(student3);

        // 开始上课,所有学生都参与学习
        school.StartClasses();

        // Student对象的生命周期独立于School对象
        // 即使School对象销毁,Student对象仍然存在并可以被其他对象引用
    }
}

1.3 延时动态对象组合

延时动态对象组合是一种设计模式,其中对象的组合并不是在对象创建时立即完成,而是在需要时动态地完成。这种模式通常用于延迟初始化,资源管理,或者当组合的对象在创建时不一定存在时。通过延时组合,可以提高系统的灵活性和性能,特别是在对象的创建和初始化成本较高的情况下。

延时动态对象组合的优势:

  • 性能优化:对象只在需要时才会被创建,节省了系统资源,特别是当对象创建代价高或对象不一定总是需要时。

  • 提高灵活性:延迟初始化允许系统在不同的时间点根据需要动态组合对象,从而可以灵活地响应运行时的需求。

  • 资源管理:避免在程序启动时创建不必要的对象,减少内存占用和初始化时间。

一个 Document 对象,其中包含一个 Printer 对象。Printer 对象只有在需要时才会被创建和初始化:

using System;

class Printer
{
    public Printer()
    {
        Console.WriteLine("Printer is initialized.");
    }

    public void Print(string content)
    {
        Console.WriteLine($"Printing: {content}");
    }
}

class Document
{
    private Printer _printer;

    // 延时初始化 Printer 对象
    public Printer Printer
    {
        get
        {
            if (_printer == null)
            {
                _printer = new Printer();  // 在需要时才初始化 Printer 对象
            }
            return _printer;
        }
    }

    public void PrintDocument(string content)
    {
        Console.WriteLine("Preparing to print document...");
        Printer.Print(content);  // 使用延时初始化的 Printer 对象进行打印
    }
}

class Program
{
    static void Main()
    {
        Document doc = new Document();

        // Document 对象创建时,Printer 对象尚未初始化
        Console.WriteLine("Document created.");

        // 只有在需要打印时,Printer 对象才会被初始化
        doc.PrintDocument("Hello, World!");

        // 如果不需要打印,Printer 对象将不会被创建
    }
}
Document created.
Preparing to print document...
Printer is initialized.
Printing: Hello, World!

解释:

  • Printer 类:定义了一个 Printer 对象,它有一个 Print 方法用于打印内容。在创建 Printer 对象时会输出初始化信息。

  • Document 类

    • 包含一个私有的 Printer 字段 _printer,初始值为 null

    • 通过一个 Printer 属性进行延时初始化。只有在第一次访问 Printer 属性时,_printer 才会被创建和初始化。

    • PrintDocument 方法中使用了 Printer 属性。当需要打印文档时,Printer 对象才会被动态创建和使用。

  • Main 方法

    • 创建了一个 Document 对象时,并没有立即创建 Printer 对象。

    • 只有在调用 PrintDocument 方法时,Printer 对象才会被初始化和使用。

    • 上述两种非延时动态对象组合的方式,在Main中将所有独立的对象都进行了初始化,或者通过对象A的初始化是,对象B也在对象A的类内部进行了初始化。

1.4 对象组合的特殊形式——自引用类

自引用类是一种特殊形式的对象组合,其中一个类包含一个对同类对象的引用。自引用类常用于构建递归结构或树形结构,如链表、树、图等数据结构。通过自引用,一个对象可以引用另一个同类型的对象,形成层次化或递归的结构。

示例:链表中的自引用

链表是一个常见的自引用结构。每个节点(Node)都包含一个数据部分以及一个指向下一个节点的引用。这种结构允许创建一个动态大小的序列。

使用 C# 实现的单向链表,其中 Node 类是一个自引用类:

using System;

class Node
{
    public int Data { get; set; }
    public Node Next { get; set; }  // 自引用,指向同类型的下一个节点

    public Node(int data)
    {
        Data = data;
        Next = null;
    }
}

class LinkedList
{
    private Node head;

    public LinkedList()
    {
        head = null;
    }

    // 添加节点到链表末尾
    public void Add(int data)
    {
        Node newNode = new Node(data);
        if (head == null)
        {
            head = newNode;  // 链表为空时,设置新节点为头节点
        }
        else
        {
            Node current = head;
            while (current.Next != null)
            {
                current = current.Next;  // 找到链表的最后一个节点
            }
            current.Next = newNode;  // 将新节点连接到链表末尾
        }
    }

    // 打印链表中的所有节点
    public void PrintAllNodes()
    {
        Node current = head;
        while (current != null)
        {
            Console.WriteLine(current.Data);
            current = current.Next;
        }
    }
}

class Program
{
    static void Main()
    {
        LinkedList list = new LinkedList();
        list.Add(1);
        list.Add(2);
        list.Add(3);

        list.PrintAllNodes();  // 输出链表中的所有节点值
    }
}

自引用类的应用场景:

  • 链表:如示例中的单向链表、双向链表、循环链表等。

  • 树结构:如二叉树、B 树、红黑树等,每个节点包含对子节点的引用。

  • 图结构:节点间包含相互引用,形成复杂的网络关系。

  • 递归结构:支持递归定义和操作的复杂数据结构。

自引用类的特点:

  • 递归结构:自引用类可以自然地表达递归结构,适用于分层或递归数据的表示和操作。

  • 动态结构:通过自引用,可以构建动态大小和深度的数据结构,如链表和树。

  • 灵活性:自引用允许对象在运行时动态地链接和操作其他同类对象,提供灵活的数据管理能力。

自引用类的设计模式在构建动态、递归或分层结构时非常有用,广泛应用于各种数据结构和算法中。

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

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

相关文章

Arduino UNO 编程 第一期——下载及使用

字幕君已上线...... 副字幕君已上线...... 计数君已上线...... 彩色字幕君 ( 花了重金请来的 ) 已上线...... Doge智能系统已上线...... Doge:嗨嗨我又来了&#xff01; 观众们......已上线&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; OK LETS GO&am…

理解大模型中的Cursor技术:优化长文本推理的前沿方案

理解大模型中的Cursor技术&#xff1a;优化长文本推理的前沿方案 随着自然语言处理&#xff08;NLP&#xff09;领域的快速发展&#xff0c;大型语言模型&#xff08;如GPT系列&#xff09;已广泛应用于文本生成、对话系统和复杂推理任务。然而&#xff0c;随着文本长度的增加…

灾难性遗忘问题(Catastrophic Forgetting,CF)是什么?

灾难性遗忘问题&#xff08;Catastrophic Forgetting&#xff0c;CF&#xff09;是什么&#xff1f; 在深度学习和人工智能领域中&#xff0c;“灾难性遗忘”&#xff08;Catastrophic Forgetting&#xff09;是指当神经网络在增量学习&#xff08;Incremental Learning&#…

用AI生成旅游打卡照!FLUX假装去旅行lora的使用【附工作流】

hello&#xff01;今天我们来聊聊一个特别有意思的话题&#xff1a;如何用AI生成那些看起来像是去过世界各地的旅游打卡照&#xff0c;还能在朋友圈里炫耀一番。很多人看到这些照片都会问&#xff1a;“你真的去过这些地方吗&#xff1f;” 而且最主要的是这种图片做点自媒体旅…

数据仓库系列13:增量更新和全量更新有什么区别,如何选择?

你是否曾经在深夜加班时&#xff0c;面对着庞大的数据仓库&#xff0c;思考过这样一个问题&#xff1a;“我应该选择增量更新还是全量更新&#xff1f;” 这个看似简单的选择&#xff0c;却可能影响整个数据处理的效率和准确性。今天&#xff0c;让我们深入探讨这个数据仓库领域…

RT-DETR+Sort 实现目标跟踪

在前一篇博客中&#xff0c;博主介绍了利用YOLOv8与Sort算法实现目标跟踪&#xff0c;在今天这篇博客中&#xff0c;博主将利用RT-DETR算法与Sort算法相结合&#xff0c;从而实现目标跟踪。。 这里博主依旧是采用ONNX格式的模型文件来执行推理过程&#xff0c;由于Sort算法是基…

vue part6

Vue脚手&#xff08;CLI&#xff09; 第一步&#xff08;仅第一次执行&#xff09;&#xff1a;全局安装vue/cli。 npm install -g vue/cli- 第二步&#xff1a;切换到你要创建项目的目录&#xff0c;然后使用命令创建项目vue create xxxx&#xff08;项目名字&#xff09; 第…

el-table利用折叠面板 type=“expand“ 嵌套el-table,并实现 明细数据多选,选中明细数据后返回原数据得嵌套格式

效果图: 废话不多说直接上代码&#xff0c;完整代码展示&#xff1a; <template><el-tableborderref"multipleTable":data"tableData"tooltip-effect"dark"style"width: 100%"><el-table-columnwidth"50"la…

线程池在接受到30个比较耗时的任务时的状态,在前面30个比较耗时的任务还没执行完成的情况下,再来多少个任务会触发拒绝策略?

目录 一、提出问题 二、解答 问题 1: 线程池在接受到30个比较耗时的任务时的状态 问题 2: 在前面30个比较耗时的任务还没执行完成的情况下&#xff0c;再来多少个任务会触发拒绝策略&#xff1f; 总结 一、提出问题 我们首先自定义一个线程池&#xff1a; new ThreadPoo…

18042 计算分段函数值

### 伪代码 1. 读取输入的实数x。 2. 根据x的值计算y&#xff1a; - 如果x < 1&#xff0c;y x。 - 如果1 < x < 10&#xff0c;y 2x - 1。 - 如果x > 10&#xff0c;y 3x - 11。 3. 输出y的值&#xff0c;保留两位小数。 ### C代码 #include <io…

fl studio 21/24破解版(水果音乐制作软件24) v24.1.1.4285附安装教程

fl studio 21/24破解版&#xff0c;又被国内网友称之为水果音乐制作软件24&#xff0c;是Image-Line公司成立26周年而发布的一个版本&#xff0c;是目前互联网上最优秀的完整的软件音乐制作环境或数字音频工作站&#xff0c;包含了编排&#xff0c;录制&#xff0c;编辑&#x…

World of Warcraft [CLASSIC][80][Grandel] Call to Arms: Warsong Gulch

Call to Arms: Warsong Gulch - Quest - 魔兽世界怀旧服CTM4.34《大地的裂变》数据库_大灾变85级魔兽数据库_ctm数据库 10人PVP战歌峡谷&#xff0c;该战场经常用来互刷军衔和荣誉&#xff0c;哈哈 wow plugin_魔兽世界挂机插件-CSDN博客

完美解决node-sass@4.14.1 postinstall: `node scripts/build.js` 问题

node v14.16.0 安装node-sass4.14.1会出现报错 看日志排查发现设置的源国内的都有问题 直接梯子下载&#xff1a; https://github.com/sass/node-sass/releases/download/v4.14.1/win32-x64-83_binding.node 本地启动phpstudy&#xff0c;当然你也可以放在你服务器上&#xff0…

学习笔记 ---- 数论分块(整除分块)

文章目录 算法概述引理引理 1 1 1引理 2 2 2 数论分块结论&#xff08;区间右端点公式&#xff09;过程 N N N 维数论分块向上取整的数论分块 例题 H ( n ) H(n) H(n)[CQOI2007] 余数求和[清华集训2012] 模积和 算法 概述 数论分块可以快速计算一些含有除法向下取整的和式(即…

掌握 SQL 数据操纵的基础技巧

在数据库管理中&#xff0c;SQL 数据操纵语言 (DML) 是至关重要的工具。它主要包括 INSERT、UPDATE 和 DELETE 语句&#xff0c;用于对数据库中的数据进行插入、更新和删除操作。本文将带你快速了解这些基本操作。 插入数据 在创建了一个表之后&#xff0c;最常见的操作就是插…

基于SpringBoot+Vue+MySQL的的宠物商城网站

系统背景 基于SpringBootVueMySQL的宠物商城网站是一个结合了现代Web开发技术的综合性电商平台&#xff0c;专为宠物爱好者及宠物商家设计。该系统背景可以从多个方面来阐述&#xff0c;包括但不限于市场需求、技术选型、用户体验以及平台价值等方面。 1. 市场需求 随着人们生…

python内置模块time详解(我们需要了解的多种时间格式)

Python的time模块提供了各种与时间相关的函数。我们可以获取当前时间、操作时间日期、计算两个时间差等。 时间有两种标准表示法: 数字表示: 整数或浮点数&#xff0c;通常是自从1970年1月1日以来的秒数。9个整数组成的元组&#xff1a;元组项包含年份&#xff0c;月份&#…

第 4 章 第 4 章 卷积神经网络-datawhale ai夏令营

独热向量 y ′ 的长度决 定了模型可以识别出多少不同种类的东西。我们希望 y ′ 和 yˆ 的交叉熵越小越好。 为了避免过拟合&#xff0c;在做图像识别的时候&#xff0c;考虑到图像本身的特性&#xff0c;并不一定 需要全连接&#xff0c;即不需要每个神经元跟输入的每个维度都…

进程通信——消息队列

文章目录 1.概念1.0 IPC1.1 什么是消息队列1.2 消息队列工作机制1.3 消息队列与其他进程通信机制的比较&#xff1a; 2.使用System-V版2.1 用户消息缓冲区2.2 创建消息队列msgget2.3 添加消息到消息队列msgsend2.4 从消息队列读取消息、2.5 消息队列的控制函数msgctrl2.6 msqid…

SpringBoot中,启动A服务,naocs却注册B服务,解决思路。

今天遇到了一个令我非常费解的报错&#xff0c;我明明启动的是auth服务&#xff0c;但是nacos愣是给我注册的patient服务&#xff0c;下面看看解决思路&#xff08;虽然我这个问题很乌龙&#xff0c;但如果真的是你的配置有问题&#xff0c;那么这篇文章也是可以帮助到你。&…