面向对象

面向过程 VS 面向对象

面向过程

  • 以 过程 为中心 的编程思想,在编程时把 解决问题的 步骤 分解出来,使用函数实现这些步骤,再一步步按顺序调用函数,直至解决问题
  • 按照面向过程的思想,在解决问题时,各函数之间是为了问题服务
  • 面向过程思想偏向于我们做一件事的流程,首先做什么,其次做什么,最后做什么

特性

自顶向下、逐步求精、模块化

image.png

面向对象

  • 以 对象 为中心的编程思想,在编程时把要 解决的问题 抽象成 各个对象
  • 建立对象的目的 不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为
  • 按照面向对象的思想,是站在提出问题的主体的角度去解决问题,可将解决这一具体问题的步骤,抽象为解决这一类问题的方法,以后再遇到类似的问题,可以复用解决思路
  • 面向对象是按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的方法
  • 通过面向对象的理念,将计算机软件系统能与现实世界中的系统一一对应

特性

封装、继承、多态、抽象

image.png

举例

需求: 写一道 蛋炒饭 的程序

蛋炒饭 和 盖浇饭

蛋炒饭: 有一道工序是把 鸡蛋和 米饭混合一起炒
盖浇饭: 点什么菜,最后就把 这道菜 和 米饭混合一起炒

如果用 面向过程 的思想,写出了程序最后就是 一份 具体 的蛋炒饭
如果用 面向对象 的思想,可以根据 需要 写出 需要的 炒饭,可以是 蛋炒饭/红烧肉盖饭/青椒土豆盖浇饭

根据 面向过程的思想,就是只 做一道 蛋炒饭 的程序
根据 面向对象的思想,把 菜 和 饭 作为 对象 抽离 出来,根据需求 输入 相应 的菜 和饭,就能得到 对应 的 炒饭程序

面向对象编程的特征

面向对象编程的四项基本原则为:

  • 抽象:抽象是简化复杂系统的过程,将实体的相关特性和交互建模为类,以定义系统的抽象表示。抽象通过抽象类和接口来实现,抽象类定义了一些具体和一些抽象的方法,而接口定义了一组方法的契约,由实现类提供具体实现
  • 封装: 封装是将对象的状态(数据)和行为(方法)捆绑在一起,隐藏对象的内部状态和功能,通过使用访问修饰符,控制允许通过一组公共成员/函数进行访问
  • 继承:继承是一种机制,允许一个类(派生类)使用另一个类(基类)的属性和方法,同时继承也支持创建类的层次结构,通过在派生类中添加、修改或隐藏成员,实现对基类的扩展
  • 多态: 多态是一种通过同一接口(方法名)来表示不同实现的机制,多态通常通过方法的重写(override)和接口实现来实现

抽象

  • 抽象是通过隐藏不必要的细节,突出重点的特征,将实体抽象为类。
  • 抽象出来的类可以使用 abstract 关键字 来定义类

将计算图形面积 的共性 抽象成 类 Shape,使用具体实现类实现面积计算

// 抽象类
public abstract class Shape
{
    // 抽象方法,用于计算图形的面积
    public abstract double CalculateArea();
}
// Circle 是 具体的实现类 
public class Circle : Shape
{
    // 圆形的半径
    public double Radius { get; set; }

    // 实现 CalculateArea 方法: 计算圆型的面积
    public override double CalculateArea()
    {
        return Math.PI * Radius * Radius;
    }
}
// Rectangle 是 具体的实现类 
public class Rectangle : Shape
{
    // 矩形的长和宽
    public double Length { get; set; }
    public double Width { get; set; }

    // 实现 CalculateArea 方法: 计算矩形的面积
    public override double CalculateArea()
    {
        return Length * Width;
    }
}

封装

  • 封装是将数据和方法封装在同一个单元中
  • 通过定义公共接口(公共方法)来控制对数据的访问
  • 封装是为了防止外部直接访问数据,防止内部数据被随意的定义和修改,保障数据的安全性
image.png
public class Person
{
    // 封装的字段,私有字段只能在类内部访问
    private string name;
    private int age;

    // 封装的属性,提供对私有字段的访问和修改
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set
        {
            // 对年龄进行一些验证
            if (value >= 0 && value <= 120)
            {
                age = value;
            }
            else
            {
                Console.WriteLine("Invalid age value");
            }
        }
    }

    // 公共方法,对外提供访问和操作封装字段的接口
    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main()
    {
        // 创建 Person 对象
        Person person1 = new Person();

        // 设置对象的属性值
        person1.Name = "John";
        // 设置 Age 的值, 但是 外界对于 Age 的一些验证并不知情
        person1.Age = 25;

        // 调用方法显示信息
        person1.DisplayInfo();
    }
}

继承

  • 基于某一个类进行扩展,使一个新类直接拥有基类的基本特征,而不需要重复去写,这就是继承的思想
  • C# 中,类只能继承一个类,一个子类只能有一个父类,一个父类可以有多个子类,若要实现多重继承,可以采用接口的形式
  • 在 C# 中,类默认继承自 System.Object 类,直接继承的是直接父类,即使没有显式指定某个类的基类,它仍然隐式地继承自 System.Object
  • C#中,使用 class 关键字来定义一个类,使用 :(冒号) 来表示继承关系
class Animal
{
    public void Eat()
    {
        Console.WriteLine("Animal is eating");
    }
}
// Mammal 类继承 Animal
class Mammal : Animal
{
    public void Sleep()
    {
        Console.WriteLine("Mammal is sleeping");
    }
}
// Dog 的直接父类是 Mammal,间接父类  Animal
class Dog : Mammal
{
    public void Bark()
    {
        Console.WriteLine("Dog is barking");
    }
}

多态

  • 多态是一种通过同一接口(方法名)来表示不同实现的机制
  • 多态性有两种主要形式:编译时多态性和运行时多态性
  • 编译时多态性(静态多态性)是编译时,通常与方法的重载(overloading)有关,同一个类中有多个方法具有相同的名称但不同的参数列表
  • 运行时多态性(动态多态性)是运行时,通常与方法的重写(overriding)有关,子类可以提供对基类方法的新实现
class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Generic animal sound");
    }
}

class Dog : Animal
{
    // 重写
    public override void MakeSound()
    {
        Console.WriteLine("Woof! Woof!");
    }

    public void Gender(int x)
    {
        if(x==1)
         {
          Console.WriteLine("Gender: 男");
         }
         else if (x==2)
         {
           Console.WriteLine("Gender: 女");
         }
         else
         {
         Console.WriteLine("Gender: 不明确");
         }
    }
    // 重载
    public void Gender(string s)
    {
        Console.WriteLine("Gender: " + s);
    }

}

class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Meow!");
    }
}

class Program
{
    static void Main()
    {
        Animal animal = new Dog();
        animal.MakeSound();  // 运行时确定调用 Dog 类中的 MakeSound 方法
        animal.Gender(1);   // 编译时确定调用 Dog 类中的 Gender(int x)方法
        animal.Gender("男")  // 编译时确定调用 Dog 类中的 Gender(string x)方法
        animal = new Cat();
        animal.MakeSound();  // 运行时确定调用 Cat 类中的 MakeSound 方法
    }
}

方法的重载

方法的重载是指在同一个类中,可以定义多个具有相同名称但参数列表不同的方法

  • 重载的方法具有相同的方法名
  • 参数列表必须不同,包括参数的类型、个数或顺序
  • 返回类型可以相同也可以不同
class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }

    public string Add(string a, string b)
    {
        return a + b;
    }
}

方法的重写

方法的重写是指在派生类中重新实现(覆盖)基类中已有的方法

  • 重写的方法具有相同的方法签名(方法名、参数列表、返回类型),并且在基类中必须是虚拟(virtual)或抽象(abstract)的
  • 使用 override 关键字进行重写
class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

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

重写和重载的区别

方法名称

重载:方法名相同,参数列表不同
重写:方法名相同,参数列表相同

发生位置

重载:在同一个类中
重写:在派生类中

关键字

重载:使用相同的方法名,不需要额外的关键字
重写:使用 override 关键字

调用方式

重载:编译器根据调用时的参数类型、个数、顺序选择合适的方法
重写:运行时根据对象的实际类型选择调用的方法

用途

重载:提供更灵活的方法调用,可以根据不同的参数执行不同的逻辑
重写:实现多态,允许派生类提供特定于其类型的实现

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容