01Realize object-oriented programming with C language

Using C language to realize object-oriented programming

The subtitle of GOF's "Design Patterns" book is called "The Foundation of Reusable Object-Oriented Software". From the title, it can be seen that object-oriented is the basic idea of ​​design patterns. Since C language is not an object-oriented language, C language does not directly provide object-oriented functions such as encapsulation, inheritance, composition, and polymorphism, but C language has struct and function pointers. We can use the data and function pointers in the struct to simulate the behavior of objects and classes.

So before officially starting the design pattern, let's take a look at how to implement object-oriented programming in C language.

This chapter gives the realization method of C language for object-oriented encapsulation, inheritance, composition and polymorphism.

1 package

Encapsulation means that the object only exposes the necessary external interfaces (here, public methods) to interact with other objects, and other attributes and behaviors do not need to be exposed, which allows the internal implementation of the object to be freely modified. This also requires the object to contain all the information it needs to operate, without relying on other objects to complete its own operations.

The following is an example of a power company to demonstrate the package.

Electricity companies produce and provide electricity. In order to gather the power of various power plants and allow users to obtain power, the power company provides two unified interfaces: 1. The power company gathers the power of various power plants, whether it is a thermal power plant, a hydroelectric power plant, a nuclear power plant, etc. Use an interface. If when a thermal power plant is transformed into a wind power plant, the realization of the power plant is completely different, but the interface remains the same, so the power company will not feel that the power plant has changed, and there is no need to transform the power company in order to upgrade the power plant system. 2. The power company provides electricity to users. Whether the user uses a toaster or a washing machine, there is an interface (power socket) between the power company and the user. The user's electrical equipment can be ever-changing, but the interface (power socket) remains the same. Therefore, the power company does not have to concern which equipment of the user is using electricity.

Code

#include <stdio.h>

struct PowerCompany {
    int powerReserve;
    void (*PowerPlant)(struct PowerCompany *this, int power);
    void (*PowerUser)(struct PowerCompany *this, int power);
};


void PowerPlant(struct PowerCompany *this, int power)
{ 
    this->powerReserve += power;
    printf("默认发电厂,发电%d瓦\n", power); 
    return;  
}

void PowerUser(struct PowerCompany *this, int power)
{
    if (this->powerReserve >= power) {
        printf("用电%d瓦\n", power);
        this->powerReserve -= power;
    } else {
        printf("电力不足,用电失败\n");
    }
    return;
}

/* struct PowerCompany 的构造函数 */
void PowerCompany(struct PowerCompany *this)
{
    this->powerReserve = 0;
    this->PowerPlant = PowerPlant;
    this->PowerUser = PowerUser;
    return;
}

/* struct PowerCompany 的析构函数 */
void _PowerCompany(struct PowerCompany *this)
{

}

main()
{
    struct PowerCompany myPowerCompany;
    PowerCompany(&myPowerCompany);

    /* 发电 */
    myPowerCompany.PowerPlant(&myPowerCompany, 1000);

    /* 用电 */
    myPowerCompany.PowerUser(&myPowerCompany, 800);
    myPowerCompany.PowerUser(&myPowerCompany, 800);
    
    _PowerCompany(&myPowerCompany);
    return;
}

It can be seen from the example of the power company that a good package can effectively reduce the coupling, and the internal implementation of the package can be freely modified without affecting other parts of the system.

2 Inheritance

One of the most powerful features of object-oriented programming is code reuse, and inheritance is one of the main means to achieve code reuse.
Inheritance allows one class to inherit the properties and methods of another class. We can construct the parent class by identifying the commonalities between things, by abstracting public properties and behaviors, and constructing the subclasses by inheriting the parent class. The parent class, the public attributes and behaviors, are reused.

The following mammalian example demonstrates inheritance.

Cats and dogs are mammals, they have common attributes and behaviors. For example, cats and dogs have eyes, and they both bark. We abstract the color and call of the eyes as the attributes of the parent class of mammals. Let cats and dogs inherit from the parent class of mammals, which can realize the reuse of "eye color" and "calling".

UML
Insert picture description here

Code

#include <stdio.h>

struct Mammal {
    int eyeColor;
    void (*ShowEyeColor)(struct Mammal *this);
    int callNum;
    void (*Call)(struct Mammal *this);
};

void ShowEyeColor(struct Mammal *this)
{
    if (this->eyeColor == 1) {
        printf("眼睛是绿色\n");
    } else {    
        printf("眼睛是蓝色\n");
    }
    return;
}

void Call(struct Mammal *this)
{
    printf("叫%d声\n", this->callNum);
    return;
}

// struct Mammal 的构造函数
void Mammal(struct Mammal *this, int eyeColor, int callNum)
{
    this->eyeColor = eyeColor;    
    this->ShowEyeColor = ShowEyeColor;  
    this->callNum = callNum;
    this->Call = Call;
    return;  
}

struct Dog {
    struct Mammal mammal;
};

// struct Dog 的构造函数
void Dog(struct Dog *this, int eyeColor, int callNum)
{
    Mammal(this, eyeColor, callNum);
    // 狗类的其它属性,略
    return;
}

// struct Dog 的析构函数
void  _Dog(struct Dog *this)
{

}

struct Cat {
    struct Mammal mammal;
    // 猫类的其它属性,略
};

// struct Cat 的构造函数
void Cat(struct Cat *this, int eyeColor, int callNum)
{
    Mammal(this, eyeColor, callNum);
    return;
}

// struct Cat 的析构函数
void  _Cat(struct Cat *this)
{

}

main()
{
    struct Dog myDog;
    Dog(&myDog, 1, 3);
    myDog.mammal.ShowEyeColor(&myDog);
    myDog.mammal.Call(&myDog);
    _Dog(&myDog);
 
    struct Cat myCat;
    Cat(&myCat, 2, 5);
    myCat.mammal.ShowEyeColor(&myCat);
    myCat.mammal.Call(&myCat);
    _Cat(&myCat); 
    
    return;
}

3 polymorphism

Polymorphism and inheritance are closely coupled, but it is usually regarded as one of the most powerful advantages of object-oriented technology. Subclasses inherit the interface of the parent class. Each subclass is a separate entity, and each subclass needs to have a separate response to the same message. Each subclass responds to the same message with the same inherited interface, but each subclass can have different implementations. This is polymorphism.

In the example of cats and dogs, cats and dogs inherit the "barking" method of the mammalian parent class, but the barking of cats and dogs are different, so cats and dogs can use different The realization of "called".

The following code demonstrates polymorphism.

Code

#include <stdio.h>

struct Mammal {
    int eyeColor;
    void (*ShowEyeColor)(struct Mammal *this);
    int callNum;
    void (*Call)(struct Mammal *this);
};

void ShowEyeColor(struct Mammal *this)
{
    if (this->eyeColor == 1) {
        printf("眼睛是绿色\n");
    } else {    
        printf("眼睛是蓝色\n");
    }
    return;
}

void Call(struct Mammal *this)
{
    printf("叫%d声\n", this->callNum);
    return;
}

/* struct Mammal 的构造函数 */
void Mammal(struct Mammal *this, int eyeColor, int callNum)
{
    this->eyeColor = eyeColor;    
    this->ShowEyeColor = ShowEyeColor;  
    this->callNum = callNum;
    this->Call = Call;
    return;  
}

struct Dog {
    struct Mammal mammal;
};

void Bark(struct Dog *this)
{
    int i;
    for (i = 0; i < this->mammal.callNum; i++) {
        printf("汪 ");
    }
    printf("\n");
    return;
}

/* struct Dog 的构造函数 */
void Dog(struct Dog *this, int eyeColor, int callNum)
{
    Mammal(this, eyeColor, callNum);
    this->mammal.Call = Bark;
    return;
}

// struct Dog 的析构函数
void  _Dog(struct Dog *this)
{

}

struct Cat {
    struct Mammal mammal;
};

void Meow(struct Cat *this)
{
    int i;
    for (i = 0; i < this->mammal.callNum; i++) {
        printf("喵 ");
    }
    printf("\n");
    return;
}

/* struct Cat 的构造函数 */
void Cat(struct Cat *this, int eyeColor, int callNum)
{
    Mammal(this, eyeColor, callNum);
    this->mammal.Call = Meow;
    return;
}

// struct Cat 的析构函数
void  _Cat(struct Cat *this)
{

}

main()
{
    struct Dog myDog;
    Dog(&myDog, 1, 3);
    
    struct Cat myCat;
    Cat(&myCat, 2, 5);

    struct Mammal *myMammal;
    myMammal = &myDog;
    myMammal->Call(myMammal);
    myMammal = &myCat;
    myMammal->Call(myMammal);

    _Dog(&myDog);
    _Cat(&myCat);

    return;
}

4 combinations

Combination and inheritance are both methods of code reuse in object-oriented, and only through combination and inheritance two methods can be used to build new classes using other classes.

In the inheritance relationship mentioned earlier, we abstracted the common attributes and behaviors as the parent class. For example, cats and dogs are mammals, and they have the attributes and behaviors common to mammals. The relationship between cats, dogs and mammals is "is-a", that is, cats and dogs (is-a) mammals.
The combination relationship reflects "has-a". Take the relationship between the house and the window as an example. We can build the window class separately, and then apply the window class to various house classes. At this time house (has-a) windows, but never windows (is-a) houses.

The following UML and code demonstrate the combination.

UML

Insert picture description here

Code

#include <stdio.h>

struct Window {
    int length;
    int width;
    void (*ShowWindow)(struct Window *this);
};

void ShowWindow(struct Window *this)
{
    printf("这是长%d厘米、宽%d厘米的窗户\n", this->length, this->width);
    return;
}

void Window(struct Window *this, int length, int width)
{
    this->length = length;
    this->width = width;
    this->ShowWindow = ShowWindow;
    return;
}

void _Window(struct Window *this)
{

}

struct House {
    struct Window *window;
    int livingRoomNum;
    int bedRoomNum;
    int bathRoomNum;
    void (*ShowHouse)(struct House *this);
};

void ShowHouse(struct House *this)
{
    printf("这是%d室%d厅%d卫的房子\n", this->bedRoomNum, this->livingRoomNum, this->bathRoomNum);
    return;
}


void House(struct House *this, int livingRoomNum, int bedRoomNum, int bathRoomNum)
{
    this->livingRoomNum = livingRoomNum;
    this->bedRoomNum = bedRoomNum;
    this->bathRoomNum = bathRoomNum;
    this->ShowHouse = ShowHouse;
    return;
}

void _House(struct House *this)
{

}

void main()
{
    struct House myHouse;
    House(&myHouse, 2, 3, 2);
    
    /* 组合是一种低耦合,如果不初始化,子类只是存放了一个空指针来占位关联。
       此处是与继承关系的区别。继承是一种强耦合,在继承关系中,无论如何子类拥有父类全部的信息。*/
    struct Window myWindow1;
    myHouse.window = &myWindow1;
    Window(myHouse.window, 100, 50);

    /* 通过获得其它对象的引用而在“运行时”动态定义 */
    myHouse.ShowHouse(&myHouse);
    myHouse.window->ShowWindow(myHouse.window);

    _Window();
    _House();

    return;
}

The difference between composition and inheritance is as follows:

The combination relationship embodies "has-a". The inheritance relationship embodies "is-a".

Combination is a "black box" reuse, which is dynamically defined "at runtime" by obtaining references to other objects. Inheritance is "white box" reuse and static definition "compile time".

This is very, very important. When we talk about specific design patterns later, we often use "run-time" dynamic definition instead of "compile-time" static definition to achieve

Guess you like

Origin blog.csdn.net/weixin_46826913/article/details/106166428