You get a bonus - 1 coin for daily activity. Now you have 1 coin

Core Principles of Designing Classes and Objects in OOP

Lecture



Developing classes and objects in OOP is the process of creating an object-oriented structure that consists of classes and objects. OOP principles help create more efficient and easily maintainable code that is easy to scale and extend.

Classes and Objects. Basic concepts and definitions. Description of a class. Definition of an object, means and examples of its description. Properties of a class and an object. Methods and rules for accessing class members and restrictions on access to class members. Definition of class methods, program examples.

Constructors and Destructors. Basic concepts and definitions, program examples. Default constructors and destructors. Principles of initializing class parameters, program examples.

Pointers and References. Pointers, basic concepts and definitions. Pointer names, the dereference operator. Pointers, addresses and variables. Memory structure: stack and dynamic. The new and delete operators, program examples

Placing objects in dynamic memory. Accessing class members, program examples. Dynamic allocation of class members. Deleting objects. The this pointer and the address-of operator &. Specifics of developing programs with pointers, program examples. References, basic concepts and definitions. Null pointers and null references. Passing function arguments by reference. Using swap.

Arrays and Pointers, Indexing, Initialization. Passing arrays to functions, program examples.

This article describes a number of principles for developing programs in an object-oriented style. Development here is understood not only as writing program code, but also as designing the structure of the program or its part, in accordance with the requirements placed on it. These requirements are based both on the personal experience of professionals and on the literature. The purpose of the article is to set out the rules that, when followed, make it possible to meet these requirements.

The article is primarily intended for those who have recently become acquainted with object-oriented programming (OOP), in particular with programming in C++. The basic requirements and principles of development are presented in it without detailed analysis, but they can serve as a good guideline for people who are just starting to program. On the one hand, it is possible to follow all of the rules set out below at once, but on the other hand, this set of rules is not set in stone, and it can and should be adapted to suit one's own needs.

Development Requirements

The main goal of development – is to obtain a program with a certain set of functionality. Naturally, it is impossible to get a finished program right away. Therefore, everything has to be done gradually, while wanting to be sure that everything done works correctly. Hence the first requirement –bring the program to a working state as often as possible. Ideally, this means that the program should always compile, run, and perform all actions correctly. But that is the ideal; in reality, even finished versions do not work as expected. As a rule, there are always a number of uncorrected errors and shortcomings. Therefore, the first thing that has to be set aside (but which one must strive for) – is the correct operation of the program. The only thing that must almost always hold – the program must not make fatal errors, or, in programmer slang, crash. In much rarer cases the program cannot be run at all. And even more rarely – compiled. Compilation – is the first check for errors in the program, and I try to compile the code as often as possible, and also check that the program runs.

Like it or not, bugs often creep into a program, and you have to find and fix them. This creates the need to go back to code written earlier. It's fine if you worked with that code last week, but often you have to look for bugs in code written much earlier. Hence the next requirement: finding bugs in the code should be as easy as possible.

Like most people, I – am lazy and don't like to work too much. So I try to write only the code that is needed at the given moment. This means that I often have to modify code already written. Of course, on the one hand this is – a drawback, but on the other hand it turns out that only rarely can you foresee everything that will be needed right away, and code written "for the future" often has to be rewritten. In fact, I borrowed this approach from Extreme Programming (XP – eXtreme Programming), where the whole development process is carried out in this way. So, we get one more requirement –the code should be open to change.

To sum up, let's list the main requirements:

  • The program should be in working order as often as possible. That is, it should compile, run, and work correctly.
  • Finding bugs in the code should be as easy as possible.
  • The code should be written so that it is easy to change.

Development Rules

Next, I will formulate the rules that I follow when writing programs:

  • The developer's main tool – pen and paper
  • The code should be understandable
  • Low coupling and high cohesion
  • Validating assumptions about function input parameters
  • Changes in small steps
  • Data encapsulation
  • Inheritance and aggregation
  • Correct initialization of objects
  • Encapsulation of change
  • Refactoring

The developer's main tool – pen and paper

By this I mean that you should always have a clear idea of what you are going to do and why. This is always the reason for writing one piece of code or another. Therefore, before you start writing code, you need to clearly picture what needs to be done. Most people perceive visual information best, so drawings – are the best way to get an idea of what you want to do. As a rule, I draw both what the user should see and what should happen in the program's code at the same time. UML is one of the generally accepted standards for the latter. A correctly drawn structure makes it easier to understand and write new code. This leads to a simpler code structure, easier bug hunting, and easier changes to the code.

The code should be understandable

The goal of this rule is clear in itself, but it is often not entirely obvious how to achieve it. I consider the following code-writing rules to be the most important:

1) There should be a single code formatting style. If you consistently stick to the chosen style, the structure of the code will be more visible.

2) All identifiers in the code should express the meaning of the concepts behind them. For example,

class Car
{
public:
void IncreaseSpeed(double acceleration);
};

much more understandable than

class MyClass
{
public:
void Method1(double);
};

3) The declaration and implementation of each class should be in separate files. That is, only one class is described in a single *.h or *.cpp file. Sometimes several classes that override one single function are inherited from one base class, and in this case there is a great temptation to put all the classes in one file, but I still prefer to stick to this rule. As a last resort, you can make two pairs of files: one for the base class, and another – for the derived ones.

Low coupling and high cohesion

This is one of the main rules of object-oriented programming. In English it sounds like "low coupling, high cohesion".

Coupling – is the mutual dependency of the implementation of classes on one another, that is, the number of changes that need to be made to classes when another class changes. Low coupling means that changes made to one class will entail only small changes to other classes. For example, if a class has a public variable widely used throughout the program, then changing the type of that variable will entail changes to a large part of the program's code. This coupling can be reduced by implementing access methods (Set... and Get...). In most cases this will allow only the access methods to be changed, and new ones added if necessary.

Cohesion – is the degree of similarity of the responsibilities of a particular class, that is, the number of types of tasks performed by the class. Low cohesion means that there is no place in the program where it makes sense to use all the methods of the class. For example, it makes no sense to add a method for calculating some complex function to a class that loads/unloads data. It is better to create a second class and use it in the first one if necessary (it can be created locally at the point where it's needed).

This rule means that each class should be focused on solving one specific task and have exactly as many connections with other classes as are needed to solve that task.

Validating assumptions about function input parameters

When developing each function, some assumptions about the input parameters are made, explicitly or implicitly. All these assumptions should always be checked, since assumptions about input parameters may not hold, for example, in the case of some error at the function call site. Pointers to some objects are often passed into functions. In this case, a null-pointer check must be performed even if you are confident that at the call sites this pointer is never null.

Cases where the assumptions about input parameters do not hold must be handled. Often this is simply a return from the function. And in cases where the assumptions must always hold, I also recommend using the ASSERT or VERIFY macros. Exceptions are sometimes used to handle such situations, but I do not recommend using them unless you know exactly what you are going to do with them. Having exceptions that can be thrown obligates the function's users to catch them in any case, which in most cases increases the amount of code (and the number of bugs).

Changes in small steps

While creating a program, you have to write a lot of new code. In the process, many different bugs appear in the code. Fixing these bugs takes a lot of effort. To simplify this process, it is better to move forward gradually, making the necessary changes in small pieces, checking the correctness of the implementation of the already finished pieces each time. This way, bugs can be localized more quickly. It is worth remembering that the compiler can be the first to report an error, so the code should be compiled as often as possible. And after a successful compilation, it is sometimes worth running the program and checking that the old functionality still works.

Data Encapsulation

Data encapsulation – is one of the fundamental principles of object-oriented programming. Data encapsulation – is the isolation of an object's data from the outside world. It is very important that an object should always be in a correct state. This is ensured, in particular, by the fact that only the object itself has access to its data, since in this case the correctness of the data can be controlled. It is worth paying special attention to the fact that classes inherited from a base class are, with respect to it, almost as external as everything else, except for a greater level of access to the object. Therefore, I recommend making all fields private and providing access to them through functions. This simplifies making changes to an object's fields, since to ensure correct operation it is enough to rewrite only the access functions.

Inheritance and Aggregation

There are two ways to use an already existing class in an object: as a base class (inheritance), and as a class field (aggregation). Inheriting a base class implies changing and/or extending its functionality, and often also using the descendant in place of the base class. Aggregation implies using a ready-made class without changing its functionality. Aggregation imposes fewer restrictions on subsequent changes to the class, so, all else being equal, I recommend using it. When choosing between inheritance and aggregation, you should ask yourself: "Is the new class a special case of the old one, or is the old one a property of the new one?". For example, when implementing a two-dimensional array via a one-dimensional one, aggregation should be preferred. Because even though the two-dimensional array will be stored as a one-dimensional one, the one-dimensional array will be its property, not its essence.

Correct Initialization of Objects

It is very important that an object remain in a correct state throughout its entire life (from the moment of creation to the moment of deletion). Therefore, it is important to initialize an object correctly. If some data is needed to create an object in a correct state, it is best to pass it into the object's constructor. This guarantees that you will not forget to pass the data needed for correct initialization of the object. If that is not possible, this must be taken into account when writing the class's implementation: every method should include a check that the class is in a correct state.

Encapsulation of Change

Changing entities are extremely difficult to work with. To simplify the work, it is better to create a separate class that encapsulates these changes. A classic example would be shared, reference-counted objects and smart pointers, which encapsulate the change in the reference count of such objects.

Refactoring

Refactoring is making changes to the code without changing its functionality. Refactoring is usually applied before implementing new functionality that requires changes to the existing code. The basic rule: first change the existing code to fit the new needs, verify its correctness, and only then introduce the new changes. This separation makes it easier to verify that the new functionality works correctly, by splitting up the changes being made.

Sometimes refactoring is carried out simply to simplify the structure of the program, but this should be done carefully, since in some cases the risk of breaking something can be too great.

Object is the name given to a mathematical representation of a real-world entity (or of a domain) that is used for modeling. Class is the name given to a very general entity that can be defined as a collection of elements.

Property (or attribute) is the name given to a propositional function defined on an arbitrary (data) type.

Method (or function) is the name given to an operation defined on the objects of a certain class.

By an object we shall mean a mathematical representation of a real-world entity (or of a domain) that is used for modeling. By a class we shall mean a very general entity that can be defined as a collection of elements (it should be noted that, in the object-oriented approach to programming, a class – is, as a rule, a primary, undefined notion, to some extent analogous to the mathematical-theoretic notion of a set, or, more precisely, of a domain). By a property (or attribute) we shall mean a propositional function defined on an arbitrary (data) type. By a method (or function) we shall call an operation that is defined on the objects of one class or another.

The concept of a class is more general than the concept of an object. An object is an instance of a class. A class can be viewed as a collection of objects (just as a set is a collection of elements). A class can be elementary or can be subdivided into subclasses (just as a set is subdivided into subsets). For example, the class PERSON contains the subclass STUDENT, which in turn contains the object John_Smith.

Classes

A user-defined reference type (similar to C++ and Java) •

Single inheritance of classes

Multiple inheritance of interfaces

• Members (elements) of a class: - constant, field, method, operator, constructor, destructor; - property, indexer, event; - static and initialized members.

• Access to class members (public, protected, private (by default), internal, protected internal)

• Initialization – via the new operator

Core Principles of Designing Classes and Objects in OOP

A class — is a set of objects related by a commonality of properties, behavior, relationships and semantics. A class encapsulates (combines) data (attributes) and behavior (operations). A class is an abstract definition of an object and serves as a template for creating objects. A graphical representation of a class in UML is shown in fig. 2.38. A class is depicted as a rectangle divided into three parts. The first contains the class name, the second — its attributes. The last part contains the class's operations, reflecting its behavior (the actions performed by the class).

Any object is an instance of a class. Defining classes and objects — is one of the most difficult tasks of object-oriented design.

Core Principles of Designing Classes and Objects in OOP

Fig. 2.38. Graphical representation of a class

The basic principles of designing classes and objects in OOP (object-oriented programming) include the following:

  1. Encapsulation: this is the principle whereby data and the methods that work with that data are combined into a single class that provides an interface for working with the object. This approach protects the data from improper use.

  2. Inheritance: this is the principle whereby classes can inherit properties and methods from other classes. This reduces code duplication and simplifies its maintenance.

  3. Polymorphism: this is the principle whereby objects can take on several forms. For example, methods can have the same name but different behavior depending on the type of object they work with.

  4. Abstraction: this is the principle whereby classes should be designed with the specific domain they must work in taken into account. This means that classes should be abstract and generalized, so as to be as universal and reusable as possible across different projects.

  5. Composition: this is the principle whereby classes should be built from smaller classes or objects. This allows components to be reused in order to quickly create new classes.

See also

  • [[b3307]]
  • [[b4875]]
  • [[b3310]]
  • [[b4171]]
  • [[b87]]
  • Object lifetime
  • Object cloning
  • Design pattern (computer science)
  • Business object (computer science)
  • Actor model

See also

avatar
22.3.2020 13:31

чтобы еще лучше понять смысл классов интерфейсов и вообще ООП, рекомендуем прочитать UML диаграммы классов
https://intellect.icu/diagramma-klassov-class-diagram-4825

Отношения классов в UML
https://intellect.icu/otnosheniya-klassov-v-uml-4301

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Object oriented programming"

Terms: Object oriented programming