The subtitle of GOF's book "design patterns" is called "the basis of reusable object-oriented software". From the title, we can see that object-oriented is the basic idea of design patterns. Because C language is not an object-oriented language, C language does not directly provide object-oriented functions such as encapsulation, inheritance, combination and polymorphism, but C language has struct and function pointer. We can use data and function pointers in struct to simulate the behavior of objects and classes.
So before formally starting the design pattern, let's see how to realize object-oriented programming in C language.
This chapter gives the implementation method of C language for object-oriented encapsulation, inheritance, combination and polymorphism.
1 package
Encapsulation means that an object only exposes the necessary external interfaces (here public methods) to interact with other objects, and other properties and behaviors do not need to be exposed, so that the internal implementation of the object can be modified freely. 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 packaging.
Power companies produce and provide electricity. In order to gather the power of various power plants and enable users to obtain power, the power company provides two unified interfaces: 1. The power company gathers the power of various power plants, and uses one interface for thermal power plants, hydropower plants and atomic power plants. When a thermal power plant is transformed into a wind power plant, the implementation of the power plant is completely different, but the interface remains the same, so the power company does not feel that the power plant has changed and does not need to transform the power company's system in order to upgrade the power plant. 2. The power company provides power to users. No matter whether the equipment used by users is a toaster or a washing machine, an interface (power socket) is used between the power company and users. The user's electrical equipment can vary, but the interface (power socket) remains unchanged. Therefore, the power company does not need to concern what 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("Default power plant%d tile\n", power); return; } void PowerUser(struct PowerCompany *this, int power) { if (this->powerReserve >= power) { printf("Electricity consumption%d tile\n", power); this->powerReserve -= power; } else { printf("Power shortage, power failure\n"); } return; } /* struct PowerCompany Constructor for */ void PowerCompany(struct PowerCompany *this) { this->powerReserve = 0; this->PowerPlant = PowerPlant; this->PowerUser = PowerUser; return; } /* struct PowerCompany Destructor */ void _PowerCompany(struct PowerCompany *this) { } main() { struct PowerCompany myPowerCompany; PowerCompany(&myPowerCompany); /* electricity generation */ myPowerCompany.PowerPlant(&myPowerCompany, 1000); /* Electricity consumption */ 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 modified freely without affecting other parts of the system.
2 succession
One of the most powerful functions of object-oriented programming is code reuse, and inheritance is one of the main means to realize code reuse.
Inheritance allows one class to inherit the properties and methods of another class. We can identify the commonness between things, construct the parent class by abstracting public attributes and behaviors, and construct each subclass by inheriting the parent class. Parent classes, that is, 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, both cats and dogs have eyes, and they both bark. We abstract the color and can be called of the eye as the attribute of the mammalian parent class, and let cats and dogs inherit the mammalian parent class, so as to realize the reuse of "eye color" and "can be called".
UML
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("The eyes are green\n"); } else { printf("The eyes are blue\n"); } return; } void Call(struct Mammal *this) { printf("call%d sound\n", this->callNum); return; } // Constructor of struct MMAL 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; }; // Constructor of struct Dog void Dog(struct Dog *this, int eyeColor, int callNum) { Mammal(this, eyeColor, callNum); // Other attributes of dogs, omitted return; } // Destructor of struct Dog void _Dog(struct Dog *this) { } struct Cat { struct Mammal mammal; // Other attributes of cats, omitted }; // Constructor for struct Cat void Cat(struct Cat *this, int eyeColor, int callNum) { Mammal(this, eyeColor, callNum); return; } // Destructor of 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 tightly coupled, but it is usually regarded as one of the most powerful advantages of object-oriented technology. Subclasses inherit the interface from the parent class. Each subclass is a separate entity. Each subclass needs to have a separate response to the same message. The response of each subclass to the same message adopts the same inherited interface, but each subclass can have different implementations, which is polymorphism.
In the case of cats and dogs, both cats and dogs inherit the "barking" method of the mammalian parent, but the barking of cats and dogs are different, so cats and dogs can adopt different "barking" implementations.
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("The eyes are green\n"); } else { printf("The eyes are blue\n"); } return; } void Call(struct Mammal *this) { printf("call%d sound\n", this->callNum); return; } /* struct Mammal Constructor for */ 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("Woof "); } printf("\n"); return; } /* struct Dog Constructor for */ void Dog(struct Dog *this, int eyeColor, int callNum) { Mammal(this, eyeColor, callNum); this->mammal.Call = Bark; return; } // Destructor of 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("Meow "); } printf("\n"); return; } /* struct Cat Constructor for */ void Cat(struct Cat *this, int eyeColor, int callNum) { Mammal(this, eyeColor, callNum); this->mammal.Call = Meow; return; } // Destructor of 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 combination
Combination and inheritance are both ways of code reuse in object-oriented, and only through combination and inheritance can we build new classes with other classes.
In the inheritance relationship mentioned earlier, we abstract the general properties and behaviors as parent classes. For example, cats and dogs are mammals. They have common attributes and behaviors of 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 house and window as an example. We can build the window class separately, and then apply the window class to various house classes. At this time, the house (has-a) has windows, but it is not a house with windows (is-a).
The following UML and code demonstrate the combination.
UML
code
#include <stdio.h> struct Window { int length; int width; void (*ShowWindow)(struct Window *this); }; void ShowWindow(struct Window *this) { printf("This is long%d Cm, width%d Centimeter window\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("This is%d room%d office%d Wei's house\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); /* Composition is a kind of low coupling. If it is not initialized, the subclass only stores a null pointer to occupy the position Association. Here is the difference between inheritance and. Inheritance is a kind of strong coupling. In the inheritance relationship, the subclass has all the information of the parent class anyway.*/ struct Window myWindow1; myHouse.window = &myWindow1; Window(myHouse.window, 100, 50); /* Dynamically defined "at run time" by obtaining references to other objects */ myHouse.ShowHouse(&myHouse); myHouse.window->ShowWindow(myHouse.window); _Window(); _House(); return; }
The differences between composition and inheritance are as follows:
The combination relationship embodies "has-a". The inheritance relationship embodies "is-a".