封装、抽象、继承、多态的多语言实现

封装(Encapsulation)

定义

  • 封装也叫信息隐藏或数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式(或叫函数)来访问内部信息或数据

  • 封装这个特性,需要编程语言本身支持访问权限控制。如 private、public 等关键字就是 Java 语言中的访问权限控制语法。private 关键字修饰的属性只能类本身访问,可以保护其不被类之外的代码直接访问。否则任意外部代码都可以直接访问、修改属性,也就没办法达到隐藏信息和保护数据的目的了,也就无法支持封装特性了

  • 即隐藏部分属性对外不可见

Python 实现

  • class MyClass:
        def __init__(self):
            self.__private_data = 42  # 使用双下划线开头定义私有属性
    
        # 公有方法用于访问私有属性
        def get_private_data(self):
            return self.__private_data
    
        def set_private_data(self, value):
            self.__private_data = value
    

C 实现

  • 参考

    • https://blog.csdn.net/qq_26768741/article/details/52563090
  • 利用头文件,在.h头文件中声明,在 .c 文件中定义,即可隐藏内部信息

    • 头文件 head.h

    • #ifndef POINT_H
      #define POINT_H
      
      struct Point;
      typedef struct Point point;
      
      point * new_project();//创建对象
      void free_point(point *point_);//释放空间
      
      #endif
      
    • 源文件

    • #include "head.h"
      
      struct Point {  //结构体中存在成员变量
          int x;
          int y;
      };
      
      point *new_point() { //相当于是一个构造函数
          point *new_point_ = (point *)malloc(sizeof(point));
          return new_point_;
      }
      
      void free_point(point *point_){ //相当于是一个析构函数
          if (point_ != NULL) {
              free(point_);
          }
          return;
      }
      
  • 利用结构体模拟

    • 结合头文件隐藏即可达到封装的效果

    • // 定义结构体用于封装数据
      struct MyClass {
          int privateData;
      };
      
      // 封装操作数据的函数
      void setPrivateData(struct MyClass* obj, int value) {
          obj->privateData = value;
      }
      
      int getPrivateData(const struct MyClass* obj) {
          return obj->privateData;
      }
      
      int main() {
          struct MyClass myObject;
          setPrivateData(&myObject, 42);
          printf("Private data: %d\n", getPrivateData(&myObject)); // 输出:Private data: 42
          return 0;
      }
      

C++实现

  • 通过关键字public、private和protected来实现

  • class MyClass {
    private:
        int privateData;
    
    public:
        // 公有成员函数,用于访问和修改私有数据
        int getPrivateData() const {
            return privateData;
        }
    
        void setPrivateData(int value) {
            privateData = value;
        }
    };
    

Java实现

  • public class MyClass {
        private int privateData;
    
        // 公有方法用于访问私有数据
        public int getPrivateData() {
            return privateData;
        }
    
        public void setPrivateData(int value) {
            privateData = value;
        }
    }
    

Go 实现

  • package main
    
    type MyStruct struct {
        PublicField  int    // 公有字段
        privateField string // 私有字段
    }
    
    // 公有方法,可被外部包访问
    func (m *MyStruct) PublicMethod() {
        // ...
    }
    
    // 私有方法,只能在内部包内访问
    func (m *MyStruct) privateMethod() {
        // ...
    }
    

JavaScript 实现

  • class MyClass {
        constructor() {
            this._privateData = 42; // 使用下划线来表示私有属性
        }
    
        // 公有方法
        doSomething() {
            // ...
        }
    
        // 私有方法
        _privateMethod() {
            // ...
        }
    }
    
    const myObject = new MyClass();
    myObject.doSomething();
    

抽象(Abstraction)

定义

  • 主要讲如何隐藏方法的具体实现,让调用者只需要关心方法提供了哪些功能,并不需要知道这些功能是如何实现的

  • 可通过编程语言提供的接口类(比如 Java 中的 interface 关键字语法)或者抽象类(比如 Java 中的 abstract 关键字语法)这两种语法机制,来实现抽象这一特性

  • 即从更通用的角度设计类,并提供对外服务,使调用方不需了解具体实现

  • 抽象类侧重代码复用,接口侧重解耦

Python 实现

  • from abc import ABC, abstractmethod
    
    # 抽象类
    class Shape(ABC):
        # 抽象方法
        @abstractmethod
        def draw(self):
            pass
    
        # 普通方法也可以有
        def common_function(self):
            pass
    
    # 实现抽象类的子类
    class Circle(Shape):
        def draw(self):
            # 绘制圆形
            print("Drawing a circle.")
    
    class Square(Shape):
        def draw(self):
            # 绘制正方形
            print("Drawing a square.")
    

C实现

  • // 抽象示例
    
    // 定义抽象函数类型
    typedef void (*AbstractFunction)(void);
    
    // 实现具体的函数
    void concreteFunctionA(void) {
        printf("This is concrete function A.\n");
    }
    
    void concreteFunctionB(void) {
        printf("This is concrete function B.\n");
    }
    
    // 使用抽象函数指针
    void useAbstractFunction(AbstractFunction function) {
        function();
    }
    
    int main() {
        AbstractFunction funcPtr;
    
        funcPtr = concreteFunctionA;
        useAbstractFunction(funcPtr);
    
        funcPtr = concreteFunctionB;
        useAbstractFunction(funcPtr);
    
        return 0;
    }
    

C++实现

  • 抽象类中可以包含纯虚函数,即在类内只声明而不定义虚函数,要求派生类必须实现这些纯虚函数。抽象类不能被实例化,只能作为其他类的基类,通过继承实现具体功能

  • // 抽象类
    class Shape {
    public:
        // 纯虚函数,要求派生类必须实现
        virtual void draw() const = 0;
    
        // 普通函数也可以有
        void commonFunction() const {
            // ...
        }
    };
    
    // 派生类实现抽象类的纯虚函数
    class Circle : public Shape {
    public:
        void draw() const override {
            // 绘制圆形
        }
    };
    
    class Square : public Shape {
    public:
        void draw() const override {
            // 绘制正方形
        }
    };
    

Java实现

  • 抽象类

  • // 抽象类
    abstract class Shape {
        // 抽象方法,要求子类必须实现
        public abstract void draw();
    
        // 普通方法也可以有
        public void commonFunction() {
            // ...
        }
    }
    
    // 实现抽象类的子类
    class Circle extends Shape {
        public void draw() {
            // 绘制圆形
        }
    }
    
    class Square extends Shape {
        public void draw() {
            // 绘制正方形
        }
    }
    
  • 接口

  • // 定义一个接口
    interface Shape {
        void draw(); // 抽象方法
    }
    
    // 实现接口
    class Circle implements Shape {
        @Override
        public void draw() {
            // 绘制圆形的具体实现
            System.out.println("Drawing a circle.");
        }
    }
    
    class Square implements Shape {
        @Override
        public void draw() {
            // 绘制正方形的具体实现
            System.out.println("Drawing a square.");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Shape circle = new Circle();
            Shape square = new Square();
    
            // 多态调用,根据实际对象的类型来选择正确的方法实现
            circle.draw(); // 输出:Drawing a circle.
            square.draw(); // 输出:Drawing a square.
        }
    }
    

Go 实现

  • package main
    
    // 定义一个接口
    type Shape interface {
        Draw()
    }
    
    // 实现接口的类型
    type Circle struct{}
    
    func (c Circle) Draw() {
        // 绘制圆形
    }
    
    type Square struct{}
    
    func (s Square) Draw() {
        // 绘制正方形
    }
    
    func main() {
        var shape Shape
    
        shape = Circle{}
        shape.Draw() // 绘制圆形
    
        shape = Square{}
        shape.Draw() // 绘制正方形
    }
    

JavaScript

  • 使用类的继承和构造函数可以实现抽象。还可以使用 static 关键字定义静态方法

  • class Shape {
        constructor() {
            // ...
        }
    
        // 抽象方法,需要在子类中实现
        draw() {
            throw new Error("Method not implemented");
        }
    }
    
    class Circle extends Shape {
        constructor(radius) {
            super();
            this.radius = radius;
        }
    
        draw() {
            // 实现绘制圆形
        }
    
        static info() {
            // 静态方法
        }
    }
    

继承(Inheritance)

定义

  • 继承用来表示类之间的is-a关系,分单继承和多继承

  • 编程语言需要提供特殊的语法机制来支持,比如 Java 使用 extends 关键字来实现继承,C++ 使用冒号(class B : public A),Python 使用 parentheses (),Ruby 使用 <。不过,有些编程语言只支持单继承,不支持多重继承,比如 Java、PHP、C#、Ruby 等,而有些编程语言既支持单重继承,也支持多重继承,比如 C++、Python、Perl 等

Python 实现

  • # 基类
    class Animal:
        def sound(self):
            # ...
            pass
    
    # 派生类继承基类
    class Dog(Animal):
        def bark(self):
            # 狗的特有行为
            pass
    
    class Cat(Animal):
        def meow(self):
            # 猫的特有行为
            pass
    

C 实现

  • 在 C 语言中,没有原生的继承机制,但可以通过结构体嵌套和函数指针来模拟实现继承的效果。我们可以在子结构体中嵌套基结构体,并使用函数指针来指向子结构体的特定实现

  • #include <stdio.h>
    
    // 基类结构体
    struct Animal {
        void (*sound)(void); // 虚函数(函数指针)
    };
    
    // 子类结构体,继承自Animal
    struct Dog {
        struct Animal base; // 嵌套基类结构体
    };
    
    struct Cat {
        struct Animal base; // 嵌套基类结构体
    };
    
    // 基类的虚函数实现
    void animalSound(void) {
        printf("Animal makes a sound.\n");
    }
    
    // 子类的虚函数实现
    void dogSound(void) {
        printf("Dog barks: Woof Woof!\n");
    }
    
    void catSound(void) {
        printf("Cat meows: Meow Meow!\n");
    }
    
    int main() {
        struct Dog myDog;
        struct Cat myCat;
    
        // 设置虚函数指针
        myDog.base.sound = dogSound;
        myCat.base.sound = catSound;
    
        // 调用虚函数,实现多态效果
        myDog.base.sound(); // Dog的声音
        myCat.base.sound(); // Cat的声音
    
        return 0;
    }
    

C++实现

  • // 基类
    class Animal {
    public:
        void sound() const {
            // ...
        }
    };
    
    // 派生类继承基类
    class Dog : public Animal {
    public:
        void bark() const {
            // 狗的特有行为
        }
    };
    
    class Cat : public Animal {
    public:
        void meow() const {
            // 猫的特有行为
        }
    };
    

Java实现

  • // 基类
    class Animal {
        public void sound() {
            // ...
        }
    }
    
    // 派生类继承基类
    class Dog extends Animal {
        public void bark() {
            // 狗的特有行为
        }
    }
    
    class Cat extends Animal {
        public void meow() {
            // 猫的特有行为
        }
    }
    

Go 实现

  • 在 Go 中,没有传统的类继承的概念,但我们可以使用组合来实现类似继承的效果。通过在一个结构体中嵌入另一个结构体,我们可以将嵌入结构体的字段和方法合并到外层结构体中,从而达到复用代码的目的

  • package main
    
    // 基类
    type Animal struct {
        name string
    }
    
    // 派生类,通过嵌入 Animal 实现类似继承
    type Dog struct {
        Animal
    }
    
    func (d Dog) Bark() {
        // 狗的特有行为
    }
    

JavaScript

  • class Animal {
        constructor(name) {
            this.name = name;
        }
    
        sound() {
            // ...
        }
    }
    
    class Dog extends Animal {
        constructor(name, breed) {
            super(name);
            this.breed = breed;
        }
    
        sound() {
            // 实现狗的声音
        }
    }
    

多态(Polymorphism)

定义

  • 多态是指子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现

  • 可通过「继承加方法重写」、「接口类语法」、「duck-typing」三种方法实现多态

  • duck-typing

    • 只要两个类具有相同的方法,就可以实现多态,并不要求两个类之间有任何关系

Python 实现

  • 由于 Python 是动态类型语言,不需要显式指定类型,所以可以根据实际对象的类型来动态绑定方法

  • # 基类
    class Shape:
        def draw(self):
            # ...
            pass
    
    # 子类实现方法
    class Circle(Shape):
        def draw(self):
            # 绘制圆形
            print("Drawing a circle.")
    
    class Square(Shape):
        def draw(self):
            # 绘制正方形
            print("Drawing a square.")
    
    # 多态调用,根据引用指向的实际对象类型来选择正确的方法实现
    def draw_shape(shape):
        shape.draw()
    
    if __name__ == "__main__":
        circle = Circle()
        square = Square()
    
        draw_shape(circle)  # 输出:Drawing a circle.
        draw_shape(square)  # 输出:Drawing a square.
    

C实现

  • 在 C 语言中,多态通常通过函数指针和函数回调来实现。函数指针允许在运行时动态绑定不同的函数实现,从而实现多态的效果

  • #include <stdio.h>
    
    // 多态函数接口
    typedef void (*PolymorphismFunction)(void);
    
    // 多态函数
    void dogSound(void) {
        printf("Dog barks: Woof Woof!\n");
    }
    
    void catSound(void) {
        printf("Cat meows: Meow Meow!\n");
    }
    
    int main() {
        PolymorphismFunction animalSounds[] = {dogSound, catSound};
    
        int choice;
        printf("Enter 0 for dog or 1 for cat: ");
        scanf("%d", &choice);
    
        // 多态调用,根据选择调用不同的函数
        if (choice >= 0 && choice < 2) {
            animalSounds[choice]();
        } else {
            printf("Invalid choice.\n");
        }
    
        return 0;
    }
    

C++实现

  • C++的多态还支持:

    • C++语言允许函数重载和运算符重载
    • C++语言通过定义虚函数来支持动态联编,动态联编是多态性的一个重要的特征
  • 多态特性的工作依赖虚函数的定义,在需要解决多态问题的重载成员函数前,加上virtual关键字,那么该成员函数就变成了虚函数,从上例代码运行的结果看,系统成功的分辨出了对象的真实类型,成功的调用了各自的重载成员函数。如果在基类某些函数前面声明为virtual,则C++编译器会在内存对象模型前面插入一个虚函数指针,该指针指向一个虚函数表,正是因为我们可以改变虚函数指针所指函数表地址,从而实现其多态操作

  • // 基类
    class Shape {
    public:
        // 虚函数
        virtual void draw() const {
            // ...
        }
    };
    
    // 派生类实现虚函数
    class Circle : public Shape {
    public:
        void draw() const override {
            // 绘制圆形
        }
    };
    
    class Square : public Shape {
    public:
        void draw() const override {
            // 绘制正方形
        }
    };
    
    // 多态调用,根据指针所指向的具体对象的类型来选择正确的函数实现
    void drawShape(const Shape* shape) {
        shape->draw();
    }
    
    int main() {
        Circle circle;
        Square square;
    
        drawShape(&circle); // 绘制圆形
        drawShape(&square); // 绘制正方形
    
        return 0;
    }
    

Java实现

  • // 基类
    class Shape {
        // 方法
        public void draw() {
            // ...
        }
    }
    
    // 子类实现方法
    class Circle extends Shape {
        public void draw() {
            // 绘制圆形
        }
    }
    
    class Square extends Shape {
        public void draw() {
            // 绘制正方形
        }
    }
    
    // 多态调用,根据引用指向的实际对象类型来选择正确的方法实现
    public class Main {
        public static void drawShape(Shape shape) {
            shape.draw();
        }
    
        public static void main(String[] args) {
            Shape circle = new Circle();
            Shape square = new Square();
    
            drawShape(circle); // 绘制圆形
            drawShape(square); // 绘制正方形
        }
    }
    

Go 实现

  • 在 Go 中,多态是通过接口(interface)实现的。由于 Go 的接口是隐式实现的,不需要显式声明类型实现了哪个接口,只要一个类型实现了接口所需的方法,它就被视为实现了该接口。这使得在 Go 中实现多态更加简洁和灵活

  • package main
    
    // 定义一个接口
    type Shape interface {
        Draw()
    }
    
    // 实现接口的类型
    type Circle struct{}
    
    func (c Circle) Draw() {
        // 绘制圆形
    }
    
    type Square struct{}
    
    func (s Square) Draw() {
        // 绘制正方形
    }
    
    func main() {
        shapes := []Shape{Circle{}, Square{}}
    
        for _, shape := range shapes {
            shape.Draw()
        }
    }
    

JavaScript实现

  • class Shape {
        draw() {
            // ...
        }
    }
    
    class Circle extends Shape {
        draw() {
            // 实现绘制圆形
        }
    }
    
    class Square extends Shape {
        draw() {
            // 实现绘制正方形
        }
    }
    
    const shapes = [new Circle(), new Square()];
    shapes.forEach(shape => {
        shape.draw(); // 根据实际类型调用不同的绘制方法
    });
    
Last Updated:
Contributors: Shiqi Lu