The Road to OOP: An Engineer's Perspective

Disclaimer



The article does not imply any fundamentally new outlook on things, except from the point of view of studying this material from "absolute zero".





The material is based on notes from about 7 years ago, when my path in studying OOP without IT education was just beginning. In those days MATLAB was the main language, much later I switched to C #.



The statement of the principles of OOP, which I found, with examples in the form of some apples, pears inherited from the "fruit" class and a bunch of terminology (inheritance, polymorphism, encapsulation, etc.), was perceived as a Chinese letter.



On the contrary, now for some reason I perceive such material normally, and the presentation from my own article at times seems confusing and long.



But my old notes and the surviving horrible code on the holodisks in the pipboy indicate that the "classical" presentation did not fulfill its functions at that time, and was completely unsuccessful. Perhaps there is something in this.



How much this corresponds to reality and your own preferences - decide for yourself ...



Prerequisites for OOP



Wall code



When I just started to write in MATLAB'e, it was the only way to write and knew how. I knew about functions and that the program can be divided into parts.



The catch was that all the examples sucked. I opened someone's course book, saw there small body functions of 2-3 lines, in total, all this did NOT work (something was missing), and it only worked when I reassembled this rubbish into a “wall”.



Then I wrote some small programs several times, and each time I wondered why there was something to share. It was only later that the understanding came: the code "wall" is the normal state of a program of about 1.5 A4 pages. No functions and, God forbid, OOP is NOT needed there.



This is how the Matlab script looks like (taken from the Internet).



Fs = 1000;                   % Sampling frequency
T = 1/Fs;                      % Sample time
L = 1000;                      % Length of signal
t = (0:L-1)*T;                % Time vector
% Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
%x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t); 
%y = x + 2*randn(size(t));     % Sinusoids plus noise
y=1+sin(100*pi*t);
plot(Fs*t(1:50),y(1:50))
title('Signal Corrupted with Zero-Mean Random Noise')
xlabel('time (milliseconds)')
figure
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
plot(f,2*abs(Y(1:NFFT/2+1))) 
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')


Dividing code into functions



Why the code is still being divided into pieces, I guessed when its volume began to become completely unimaginable (now I found shit code in the archive - 650 lines by a wall). And then I remembered about the functions. I knew that they allowed you to split your code into small blocks that are easier to debug and reuse.



But the trick is different - for some reason, all the teaching materials are silent about HOW MUCH a function of variables has ...



A mathematics course said that a function is y = f (x)



This is called a "function of one variable." For example, y = x 2 is a whole PARABOL!

Math problem: construct a PARABOL by points. In a notebook sheet, in a box.

. z=f(x,y). — — . , .. . .









, « », . , , . – . .



-…



image


And if the function has four or more variables…. Superstring theory. Calabi-Yau variety. Mortal. Not given. Understand ...



In short, this is all wrong. In programming, the normal state of a function is double vaginal double anal . It takes 100 variables and returns the same, which is fine. Another thing is abnormal - to list them with a COMMA.



image


About the fact that you can write somehow differently, I realized when I naval HERE THIS




function work = SelectFun(ProtName,length_line,num_length,angleN_1,angleN_2,num_angleN,angleF_1,angleF_2,num_angleF, res_max, num_res,varargin)
global angleF angleN model_initialized


A bunch of variables separated by a COMMA. And the calling code has completely different names for these parameters, something like SelectFun (a, b, c, d….) Therefore, you need to remember where which variable is. And make their arrangement through the COMMA. And if the code is being modernized, and the number of variables changes, then they must be rearranged again with a COMMA.



And why were global (shoot!) Variables in this squalor?



Bingo! In order not to arrange variables with each code upgrade through a COMMA.



But the COMMA was still following me like in a nightmare.



image


And varargin appeared. This means that I can add many more arguments in the calling code with a COMMA ...



And then I thought about arrays. Tutorial examples excitedly talked about the fact that an array can be like this:




=
[1 2 3
 4 5 6
 7 8 9]


And you see, X (2,3) = 6, and X (3,3) = 9, and we ... we can organize matrix multiplication on such arrays! In the last lesson we went through PARABOLS, and now MATRIXES….



And not a single line of these fucking textbooks is short and clear: you need arrays in order to make a function of 100 variables and not fall from their listing through a COMMA.



image


In general, I came up with the idea to cram everything into one big two-dimensional table. Everything went well at first:




angles =
[angleN, angleN_1, angleN_2, num_angleN
 angleF, angleF_1, angleF_2, num_angleF]

function work= SelectFun(ProtName, length_line, num_length, angles , res_max, num_res, varargin)


But I wanted more. And it started to look like this:




data=
[angleN, angleN_1, angleN_2, num_angleN
 angleF, angleF_1, angleF_2, num_angleF
length_line, num_length,  0, 0 
res_max,num_res, 0,0]
function work= SelectFun(ProtName,data,varargin)


And everything seems to be fine, but ... ZERO! They appeared because I wanted to scatter heterogeneous data over different lines, and the amount of data of different types was different ... And how should the function process these zeros? What happens if I want to update the code? I'll have to rewrite the handler for these nasty zeros inside the function! After all, some of the variables may actually be equal to zero ...



I never asked for this ...



In general, this is how I learned about STRUCTURES.



Structures



This is where it was necessary to start the presentation about methods of data packaging. Arrays with a "table", apparently, historically arose first, and they write about them too - at the beginning. In practice, you can find a lot of programs where arrays as a "table" are either one-dimensional, or they are absent at all.

The structure is a "file-folder" packing of data, approximately on the hard disk of a computer.

Drive D: \

X (variable-folder - "object" or "structure")

- a.txt (variable-file with data - "object field", English field. Number 5

is stored) - b.txt (number 10 is stored )

- .txt

Y (variable-subfolder - "object")

- d.txt (number 2 is stored)

- e.txt



To make it clearer, let's write down how we would see the path to the d.txt file in Windows Explorer

D: \ X \ Y \ d.txt


After that we open the file and write the number "2" there.

Now - how it will look in the program code. There is no need to refer to the "root local drive", so D: \ is simply not there, nor will we have a file extension. As for the rest, a period is usually used instead of a slash \ in programming.

It turns out like this:




X.Y.d=2
%   
X.a=5
X.b=10 
 - 
X.c=X.a+X.b    %..  .=5+10=15
X.Y.e=X.c*X.Y.d    %.. X.Y.e=15*2=30


In the matlab, structures ( struct ) can be created right on the spot, without leaving the checkout, i.e. the code above is executable, you can drive it into the console and everything will work right away. The structure will appear immediately, and all the "variable-files" and "variable-subfolders" will be added there at once. Unfortunately, it is impossible to say so about C #, the structure ( struct ) is set there by hemorrhoids.



The structure is a cooler relative of the TABLE ARRAY, where instead of indexes - a file-folder system. Structure = "variable-folder", which contains "variables-files" and other "variables-folders" (ie, sort of subfolders).



Everything is familiar, everything is exactly the same as on a computer, folders, files in them, only in files there are not pictures, but numbers (although pictures are also possible).



This is a more advanced version of storing data for passing to a FUNCTION compared to the idea of ​​making an ARRAY TABLE, especially two-dimensional, and, bother me, a tesseract, three- and more-dimensional.

The ARRAY TABLE is usable in two cases:

- it is small (why is it then? What, you cannot pass arguments separated by commas to the function?).

- either you can make a loop on it and automate the search / filling (this is not always possible)

In reality, ARRAY TABLE is usually used only as a one-dimensional row of homogeneous data. Everything else in normal programs is done according to the "file-folder" scheme.



Then why do programming textbooks start with arrays and tables? !!!



In short, "having discovered" the structures for myself, I decided that I had found a gold mine and urgently rewrote everything. The shit code began to look something like this:




Data.anglesN=[angleN, angleN_1,angleN_2, num_angleN]; %  
Data.anglesF=[angleF, angleF_1, angleF_2, num_angleF]; %  
Data.length_line= length_line;
Data.num_length= num_length;
Data.res_max= res_max;
Data.num_res= num_res;
function work= SelectFun(ProtName,Data,varargin)


Yes, you can do perfectionism here and make a bunch of nested objects, but that's not the point. The main thing is that now, inside the function, the variable is indexed not by its ordinal number (where it is in the argument list, separated by a COMMA), but by name. And there are no dumb zeros. And the function call is now of an acceptable form, there are only 2 COMMANDS, you can breathe out calmly.



Classes



The concept of "class" brought down a ton of terminology on me: encapsulation, inheritance, polymorphism, static methods, fields, properties, ordinary methods, constructor ... # @% !!! ..

Out of inexperience, having figured out structures, I decided that there was no need to complicate entities unnecessarily, and thought - "classes are like the same structures, only more complicated."



To some extent, it is so. More precisely, this is exactly what it is. A class, if you look very deeply, is a STRUCTURE (an ideological descendant of an array by a table), which is created when the program is STARTED (in general, it seems to be, and not only at startup). As in any descendant of TABLE ARRAY, data is stored there. They can be accessed while the program is running.



Therefore, my first class was something like this (I am writing an example in C #, in matlab, static fields are not normally implemented, only through a hack curve with persistent variables in a static function).



public class Math{
	public static double pi;
	public static double e;

	public static double CircleLength(double R){   //.. « »
	return 2*Math.pi*R; //  
    }
}


The above case is, as it were, the "basic" skill of a class - to be stupidly an array (structure) with data. This data is thrown into it at the start of the program, and from there they can be extracted, in exactly the same way as we pulled them out of the structure above. The static keyword is used for this .



The -> structure is created anywhere and stores data that is entered into it at any time.



The class -> is the structure that is created when the program starts. All fields marked with the word static simply store data, as in a normal structure. Static methods are simply functions that are called from a class, just like from a folder.




double L=Math.CircleLength(10); //L=62,8
Math.pi=4; //


I had a gag - if fields are variables and methods are functions, then how are they stored in one place? As I understand it, a function (method) in a class is actually not a function, but a pointer to a function. Those. it is about the same "variable" as pi in terms of working with it.

In short, at the beginning I understood the classes exactly in this volume and wrote another portion of shit code, where ONLY static functions were used. Otherwise, as a folder with functions, I did not use classes at all.



This point was also facilitated by the fact that this is exactly how classes are done in MATLAB - like such a stupid folder whose name begins with @ (like @ Math, without a space), inside it, the real files with the .m extension are functions (methods) and there is header file with the extension .m, which explains that the CircleLength function really belongs to the class, and is not just a .m file with a non-OOP function thrown in there.

@ Math% folder

- Math.m% header file

- CircleLength.m% function file

Yes, there is a more familiar way for a normal person to write a class in one .m - file, but at first I did not know about it. Static fields in matlab are only constant, and are written once when the program starts. Probably in order to protect against the "trawl", which decides to assign Math.pi = 4 (IMHO, absolutely useless and stupid topic, no normal person will write a large project in matlab, and a programmer will debug a small project and so, it is unlikely he is an idiot).



But back to the topic. In addition to static methods, the class also has a constructor. A constructor is basically just a function like y = f (x) or even y = f (). It may not have input arguments, there must be output arguments, and this is always a new structure (array).



What the constructor does. He just makes structures. Logically it looks like this:



C # code Approximate Boolean Equivalent (Pseudocode)


class MyClass {
    int a;
    int b;
    public  MyClass() {
	this.a=5;
	this.b=10;
    }
}



class MyClass {
    public  static MyClass MyClass() {
        int this.a=5;
        int this.b=10;
        return this;
    }
}



//… -   
var Y=new MyClass();	



//… -   
var Y= MyClass.MyClass();	






Shit code on matlab, making similar structures without any classes (where the class is present - see below):




function Y=MyClass() %  MyClass,   Y=F()
    Y.a=5
    Y.b=10
end
… -   
Y=MyClass()


And at the output we have the structure

Y (folder variable)

- a (file variable, equals 5)

- b (file variable equals 10)

From this, in fact, it is clear that the so-called class fields (not static, without the static key code ) are local variables declared inside the constructor function. The fact that they are written for some kind of devil not in the constructor, but outside, is SYNTAXIC SUGAR.



SYNTAX SUGAR - such bullshit features of a programming language, when the code starts to look as if they want to obfuscate it right when it is written. But on the other hand, it becomes shorter and faster (supposedly) written.



Having made this "discovery", I, who at that time wrote only in Matlab, was incredibly surprised.



In matlab, as I wrote above, these structures can be created in place, without any constructors, simply by writing Ya = 5 , Yb = 10, just like you in the operating system can make files and folders without leaving the cash register.



And here - some kind of "constructor", and all the fields of the structure (in the matlab they are called properties - properties, although, strictly speaking, properties are a more obscure thing than fields) need to be bureaucratically written in the header file. What for? The only benefit that I then saw in this system is that the structure fields are predefined, and this is like "self-documentation" - you can always see what should be there and what should not be there. Here's something like this I wrote then:




classdef MyClass
    properties %   
        a
        b
    end
    methods % 
        function Y=MyClass() %  . 
        %    () Y   a, b
            Y.a=5;
            Y.b=10;
        end
    end
    methods (Static) %  
        function y=f(x) %  
            y=x^2; %    ,    !11
        end
    end
end


Those. you understood everything correctly: the methods are only static, the xs constructor is for what (it is written in the documentation - Oh, classes must have a constructor - well, here's a constructor for you), I stupidly did not know everything else and decided that I had learned Zen and OOP.



But nevertheless, it seemed to me a cool idea to collect functions (static methods) by classes-folders. there were a lot of them, and I sat down to write shit code.



Bureaucracy



And ran into such a thing. There is a set of functions of some lower level of logic (they are static and are packed into classes-folders, now we will omit the names of the classes):




Y1=f1(X1);
Y2=f2(X2);
Y3=f2(X3);
Y20=f20(X20);


In small projects, it is impossible to achieve such a dominance of functions, educational examples generally contain 2-3 functions - like "see how we can build a PARABO.



And here - a fucking cloud of functions, and each of them, their mother, each has an output argument, and what to do with them all? Put in functions of a higher ("leading") level of logic! Usually there are much less of them (conventionally, 5 instead of 20). Those. conventionally, you need to somehow take these Y1, Y2, Y3… .Y20 and REMOVE them into some Z1, Z2… Z5. So that later you can make a meeting of the party and at it:




A1=g1(Z1);
A2=g2(Z2);
A5=g5(Z5);
% ,  .  , !


But Z1… Z5 don't just come by themselves. To create them, you need FUNCTIONS-WRAPPERS. Conventionally, they work something like this ...




function Z1=Repack1(Y1,Y7, Y19)
    Z1.a=Y1.a+Y7.b*Y.19.e^2;
    Z1.b=Y7.c-Y19.e;
    %....  -      Y1, Y7, Y19 
    %    Z1. 
    %        Z2…Z5, 
    % 4 .  !
end


And then there may be another "management" level ...



In short, I realized that I was in a logistic hell. I could not normally extract data from a FIGURE CLOUD of small functions y = f (x) without writing another FIGURE CLOUD repackaging-bureaucratic functions, and when the data is transferred to a higher level, we need more RAPPERS. The final program is crammed with bureaucracy through and through - there are more repackers than "business code". The folder-for-functions classes do not solve this problem - they just collect the bureaucratic idiot repackers in heaps.



And then I decided to modernize this shitty code, and it turned out that without sawing the entire bureaucratic part, this is impossible!



Just like life in Russia ...



I realized that I was doing something wrong, and understood OOP better. And the solution - if you look at it this way, it was ideologically on the surface.



OOP idea



Why do a bunch of functions like y = f (x) that produce DIFFERENT output arguments Y1… .Y20 , when you can make ONE argument. Sort of:




Y_all=f1(Y_all, X1); 
Y_all=f2(Y_all, X2);
….
Y_all=f20(Y_all, X20);


Then absolutely all the results of the function will be pushed into one structure, into one array, just into its different compartments. All. Then Y_all can be transferred directly to the top, to the upper level of the "management".




Y_all=DO_MOST_IMPORTANT_SHIT(Y_all, options_how_to_do_this_shit)


All-all-all functions-SEALERS-BUREAUERS go to the ass! All data is collected in ONE Y_all base , all low-level functions put the fruits of their labors in different Y_all compartments , the “management” scamper through all Y_all compartments and does what it should do. Nothing superfluous, the code is written quickly and works great ...



This is exactly the idea of ​​OOP and consists. In textbooks, they write educational examples about apples and pears, and then show a program in 5 lines. There is no need for any OOP at all, in the examples for 5 lines, since the transfer of data to the "top management level" is done directly without problems.



OOP is needed when a large project is the problem of "bureaucratization" ....

But back to the point. In real OOP, there is SYNTAX SUGAR. The above example with Y_all used just structures, functions f (,,,) will be considered static. OOP is a set of sugar when the code starts to look like this:




Y_all.f1(X1); %   Y_all=f1(Y_all, X1), 
Y_all.f2(X2); 
….
Y_all.f20(X20);
Y_all.DO_MOST_IMPORTANT_SHIT(options_how_to_do_this_shit);


Those. we kind of decided to bring a muddy syntax in which you can not write Y_all 2 times, but do it only 1 time. For repetition is the mother of stuttering.



The rest of the explanation "how OOP works" boils down to explaining how syntactic sugar works.



How OOP Syntactic Sugar Works



First, this database Y_all , obviously, needs to be created before it goes as an argument to the function. This requires a constructor.



Secondly, it is advisable to foresee, preferably in advance, what "compartments" it will have. As long as the Y_all database is small, this setting is annoying. I would like to dream about "classes created on the fly", in much the same way as in MATLAB you can make structures with simple commands Ya = 5 , Yb = 10 . But the desire to fantasize about this topic disappears after debugging a healthy project.



Next - calling the method (function).



This is how it approximately evolved

Function Comment
Y = f (X) This was the case in mathematics when we plotted a PARABOL by points!
X = f (X) We were bullied by bureaucrats, and we have one batch of one argument for all occasions, storing all the input and output data in different compartments inside
f (X) Why should a function return an argument? This is the archaism of the times of math lessons! And a pointless waste of memory! Let the data be passed by reference, then the function itself will come to the argument, change and leave. NOTHING = f (X)

Not the mountain goes to Mohammed, but Mohammed to the mountain.
X.f () We just pulled the X argument out with syntactic sugar. NOTHING = X.f (NOTHING)




Now - how is such a function internally arranged that takes NOTHING and returns NOTHING (the void keyword in C #).



I like how it is done in matlab (from the point of view of understanding): the function that we call as Xf () is written internally as

Sample MATLAB Code Sample C # Code

function f(this)
    % . 
    this.c=this.a+this.b;
end	



public void f() {
    this.c=this.a+this.b;
}


« » . — ( , this, fuck, shit).

this, .

« » . , ( )!

! , « this». «» this ( ).





Here is a function with "the default argument is this ", lying in the class, like in a folder - there is an ordinary method (xs, as it is correctly in Russian).

In fact, cramming all arguments into a single this is not always correct. Sometimes you need some other arguments (for example, this is user input):




public void f(int user_input) {
    this.c=this.a+this.b + user_input;
}


Sometimes you even need to return an argument (for example, about the success or failure of an operation), and not write void . Which, however, does not change the statistics: most OOP functions return NOTHING ( void ) and accept either nothing (the default argument does not count) or very few arguments.



Let's write the final code

in MATLAB




classdef MyClass<handle %  handle      
    properties %   
        a
        b
    end
    methods % 
        function this=MyClass(a, b) %  . a, b -  
            this.a=a
            this.b=b
        end
        function f(this)
            this.c=this.a+this.b
        end
    end
end
%  -  Untitled.m 
X=MyClass(5,10);
X.f();
fprintf(‘X.c=%d',X.c) % .=15


Now in C #:




public class MyClass {
    public int a;
    public int b;
    public MyClass(int a, int b) { //  . a, b -  ()		
        this.a=a;
        this.b=b;
    }
    public void f(this) {
        this.c=this.a+this.b
    }
}
//  -  
MyClass X=new MyClass(5,10);
X.f();
Console.WriteLine(“X.c={0}”,X.c);  // .=15


When I figured it out, it seemed like most of the problems with writing code faded into the background ...



Properties vs fields



Let's look at an example.

without properties with properties

MyClassA{
    int a; // field ()

    public int Get_a(){
        return this.a;
    }    
     
    public void Set_a(int value){ 
    //   - 
    //, ,  value>0
        if (value>0) this.a=value;
        else this.a=0; 
    }
}



MyClassA{
    int a; // field ()

    public int A{
       get{return this.a;}
       set{ 
           if (value>0) 
               this.a=value;
           else 
               this.a=0; 
           }
    }
}



MyClass X=new MyClassA();
X.Set_a(5);
int b=X.Get_a();



MyClass X=new MyClassA();
X.A=5;
int b=X.A;


comment: the argument

Set_a can be called whatever

Set_a (int YourVarName)

comment: a variable inside

set {...} should always be called value



This thing is quite convenient and often used, but it is still SYNTAX SUGAR.

Field is a fully qualified variable. Property is 2 class methods (get and set), the call syntax of which copies "variable call".



In fact, inside get and set, you can do crap:




int A {
    get{ return 0;}
    set{ Console.WriteLine(""); }
}


Therefore, it seems like it is recommended to write the name properties with a capital letter, and fields with a small letter.



It happens (for example, you cannot create a field in interfaces) that you need to do property quickly, then you can:




int A { get; set;} //  , -  _a
// set  get     .
public int B { get; private set;} //    
//(  ,      )


Inheritance, encapsulation, polymorphism



Why haven't you mentioned them before? Because

- in fact, when writing code, they are not in demand with such force as they are mentioned in the query "Ok Google, what is OOP". I would even say that at first they are practically fucking unnecessary .

- where they are needed, you can read about them (only the lazy did not write about this case).
When there is a process of mastering OOP-style writing skills

- you will have most classes WITHOUT inheritance. You just write ALL the functionality in the required class, and you don't really need to inherit something.

- accordingly, polymorphism (a lotion to inheritance) also goes through the forest

- "encapsulation" is reduced to assigning public everywhere (to all fields, properties and methods).

Then your hands will grow to your shoulders, and you will figure it out yourself, without this article , where you should NOT do this, especially where you should NOT write public.



But still a brief overview of them.



Inheritance. This is a clever copy-paste



A flawed implementation of "inheritance" looks like this:

Oh, my shit code has a class called MyClass, and it is missing one more SHIT field and another DO_THE_SHIT () method!

* Ctrl + C, Ctrl + V

* A new class MyClass_s_fichami is created and the desired is added there

Still, we are more civilized people, and we know that it is better not to copy the text of the program, but to refer to it.



Let's say we still write in some ancient programming language or are not aware of such a thing as "inheritance". Then we write 2 different classes


public class MyClassA{ 
    public int a;
    public void F1(int x){
    //   
        this.a=this.a*3;
    }
    public MyClassA(int a){ //
        this.a=a;
    }
}



public class MyClassB { 
    //
    private  MyClassA fieldA;
    // get  set     
    // a - .. property
    public int a{ 
        get { return fieldA.a; }
        set { this.fieldA.a=value; }
    }
    public int b;
    //   
    // «»
    public void F1(int x){ 
       this.fieldA.F1();
    }
    public void F2(int x){
        //  
        this.b=this.a*this.b;
    }
    //
    public MyClassB(int a, int b){ 
        this.fieldA= new MyClassA();
        this.a=a;
        this.b=b;
    }
}



//-   
var X=new MyClassA(5);
X.F1(); // X.a   15
Console.WriteLine(X.a); // 15	



//-   
var X=new MyClassB(5,10);
X.F1();// X.a   5*3=15
X.F2();// X.b   15*10=150
Console.WriteLine(X.a); // 15
Console.WriteLine(X.b); // 150




What we did on the right is inheritance. Only in normal programming languages ​​this is done with one command:




public class MyClassB : MyClassA { 
    //    MyClassA  , 
    //      base
    
    // a (, , property a)  , 
    //      (.    )
    public int b;
    public void F2(int x){ //  
        this.b=this.a*this.b;
    }
    public MyClassB(int a, int b){ //
    //   base    A 
    //     
        this.a=a;
        this.b=b;
    }
}


The code works "outside" in exactly the same way as in option 2. Ie. the object, as it were, becomes a "matryoshka" - inside one object another object is stupidly sitting, and there are "communication channels", pulling on which, you can refer to the internal object directly.



Spoiler header
image



In matlab, the situation is somewhat more interesting. When you run the child constructor, MyClassB , there is no quiet call to the MyClassA ancestor constructor .



You need to create it directly. On the one hand, this is annoying:




classdef MyClassB<MyClassA
    % ... 
    function MyClassB(a, b)
        this@MyClassA(a); %   ,   «»
        this.b=b;
    end
end


But if the descendant is called with other arguments at all, such as MyClassB (d) , then you can do a conversion inside, something like:




classdef MyClassB<MyClassA
    % ... 
    function MyClassB(d)
        a=d-5;
        this@MyClassA(a); 
        this.b=d+10;
    end
end


In C #, this cannot be done directly, and this gives rise to the need to write some kind of "conversion functions":




class MyClassB:MyClassA{
    //...  
    static int TransformArgs( int d) {return d-5;}
    MyClassB(int d):base(TransformArgs(d)) {this.b=d+10;}
}


or do "static constructors" like this:




class MyClassB:MyClassA {
    //...  
    MyClassB(){} //    
    static MyClassB GetMyClassB(int d) {
        var X=new MyClassB(); //    
        //   
        .a=d-5;
        .b=d+10;
        return X;
    }
}


It seems about inheritance, basically everything.



Naturally, no one forces the inheritor to write the " F1 " method and the " a " property so that they are necessarily translated into the call of the ancestor's method and fields. Broadcasting is just the default "inheritance" behavior. You can (of course! These are other methods in another class, bro) write like this:








public class MyClassB : MyClassA {
    public int a{ //   
        get { return 0; }
        set { base.a=0; }//    this.fieldA.a=0;
    }
    public int b;
    public void F1(int x){ //     «»
        //   - base -  
        Console.WriteLine(“”);//     
    }
}


Encapsulation



... Conceptually, this means that inside an object of class MyClassB in the base field sits an object of class MyClassA, with the ability to broadcast control commands outside. All this has been written above and does not make sense to repeat.



There is such a topic with different access modifiers - public , private , protected ... About them, what is most interesting, it is written everywhere more or less normally, I recommend just reading about it.

public - this will mean that the field , property or method will be visible from the outside and can be pulled.

If you DO NOT know what to do, write public (bad advice, yes).



Then find the strength in yourself and throw out this public (or, for clarity, replace it with private ) wherever it is unnecessary (do a "refactoring"). Yes, of course, it is very good to be a visionary, to act in the battle of psychics and to guess right away where to make private .

private - this means that the field , property or method of a "file-folder" object is visible only from within the methods of this class.

BUT ... It is a class, not an INSTANCE (object). If you have a code like:




class MyClassA{
    private int a=10;
    public void DO_SOMETHING(MyClassA other_obj) { 
    // DO_SOMETHING          
    //  private      MyClassA.
        this.a=100; //    
        other_obj.a=100; //  
    }
}
var X=new MyClassA();
var Y=new MyClassA();
X.DO_SOMETHING(Y);  //  X.a=100, Y.a=100


Such a thing is used in cloning (see other sources for more details).



I tried to think about this arrangement of public and private when writing code . This is an unacceptably time-consuming code when roughing out the code. And then it turns out that the code itself needs to be done in a fundamentally different way.



If the code is written in solo, then it makes no sense to bother with private and public ahead of time, there are more important tasks, for example, actually come up with and write the code ...

The only place where it is more or less clear in what place to put private and public are the same notorious properties that refer to some kind of field.




class MyClassA{
    //  private
    private int a; //"private"  C#     .
    //   public
    public int A {get{...;} set{...;}} //   ""
}


In other places, in order to arrange public and private, you need to really look at what the program is doing, and most likely it will not work to learn this "in absentia".

protected - this means " public " for all methods of the derived classes and " private " for everything else.

In general, it is logical if we assume that inherited classes appear as simply "more sophisticated versions" of their ancestors.



Honestly, I've already forgotten where I explicitly applied this protected. Usually either public or private. Most of the classes I wrote did not inherit from any other custom classes, and where they did, there was rarely any serious need for such things.



The impression is that non- public modifiers are in demand when working on some large project, which may be supported by a bunch of people ... The understanding of where to apply them appears only after a long time of sticking into a kilometer-long code. When studying “by correspondence” it is difficult to somehow give this understanding.



Polymorphism



When I was writing in Matlab, I could not understand why polymorphism is needed at all and WHAT IT IS.

Then, when I switched to C #, I realized that this is a feature of STRICTLY TYPICAL LANGUAGES, and it has a very weak relation to OOP. In matlab, you can write everywhere without knowing about the existence of this polymorphism - there is no strict typing.



For simplicity, let the classes be called A and B




class A{...}
class B:A{...}
A X=new B();
//  x  A,   -   B. 
//   .
B x_asB=new B();
A x_asA=(A) x_asB;


This is called typecasting. In C #, you can YOURSELF (if you know how) write your own custom self-made type casting systems, of almost any type to any other.

Here - just "casting" out of the box. Since another object of class A sits inside an object x belonging to class B , then one of the seemingly obvious ways of casting is to close all connections from an external object to an internal one. It's not really necessary to do this, but those who invented "polymorphism" decided that it would be most obvious to do so. And the user will write the rest of the options himself. Sorry for the (no longer quite relevant) "politota" of the 2008-2012 sample.










lass  {...}
class  :  {...} 
  = new Me (); //   
  = () ; //    


Interface



We must start with how to apply THIS.



Let's say we have a list and we want to put something in it.



In matlab, the easiest way to do this is (called a cell array):




myList={1, ‘2’, ‘fuck’, ‘shit’, MyClassA(), MyClassB(), …. ,_, _};


You don't think what kind of object it is, you just take it and put it on the list.



Next, let's say you need to loop through the list and do something with each element:




for i=1:length(myList)
      item=myList(i);
      %   -   item-
      DoSomeStuff(item);
end


If the DoSomeStuff function is smart enough to digest whatever is fed to it, this code WILL BE FULFILLED.



If the DoSomeStuff function (or its author) does not shine with intelligence, then there is a possibility of choking on something: a number, a line, your self-made class, the Bald Devil or - God forbid - your Grandmother.



MATLAB will show red swearing in English in the console and terminate your program. Thus, your code will automatically receive a Darwin Award.



However, this is actually bad because sometimes the code is very complex. Then you will be firmly convinced that you did everything correctly, but in fact, the erroneous combination of actions was simply never launched during testing.



That is why (although not only because) on MATLAB - I managed to make sure of this myself (approximately as on KPDV), on the terrible size of the code - there is NO need to write large projects.



Now let's move on to C #. We make a list, and ... and we are asked to immediately indicate the TYPE of the object. We create a list of type List.



In such a list you can put the number 1.



In such a list you can put the number 2 and even, God forgive me, 3.




List<int> lst1=new List<int>().
lst.Add(1);
lst.Add(2);
lst.Add(3);


But text strings are no longer there. Objects of your self-made class - strictly not. I am silent about the Bald Devil and your Grandmother, they cannot be there under any variant.



You can make a separate list of lines. You can - for your self-made classes.




List<MyClassA> lst2=new List<MyClassA>();
lst2.Add(new MyClassA());


In fact, you can make lists - separately - of Bald Devils, your Grandmothers.



But adding them into one list will not work. Your code will win a Darwin Prize, combined with compiler abuse before you even try to run it. The compiler prudently does not allow you to make the DoSomeStuff (item) function , which will "choke" with its argument.



This is really handy in large projects.

But what to do when you still want to put it in one little list?



This is not really a problem. It is enough to convert everything to type object . Almost (or even absolutely) everything can be converted to type object .




List<object> lst=new List<object>();
lst.Add((object) new MyClassA());
lst.Add((object) new MyClassB());


The problem starts when we start looping through the list. The point is that the object type (almost) cannot do anything. It can only BE of type object .

- What can you do?

- I can sing and dance

- And I - Sancho ...

- What can you do, Sancho?

- I am Sancho.

- Well, can you do anything?

- You do not understand. I can be Sancho.



Therefore, the interface is written. This is the class to inherit from. The interface contains method and property headers.



In our case, these are the methods and properties that ensure the NORMAL operation of the DoSomeStuff (item) function . The interface does not implement the properties themselves. This is done on purpose. In fact, one could simply inherit from some class that can be used by the DoSomeStuff () function . But that means extra code and a forgetful programmer.



Therefore, if a fellow programmer inherited an interface, but forgot to implement the required properties and methods of a class, the compiler will write it out to the code with a Darwin Prize. Thus, you can do this:




interface ICanDoTheStuff {...};
class MyClassA: ICanDoTheStuff {…}
class MyClassB: ICanDoTheStuff {…}
static void DoSomeStuff(ICanDoTheStuff item) {…}

List<ICanDoTheStuff> lst= new List<ICanDoTheStuff>();
lst.Add(new MyClassA());
lst.Add(new MyClassB());

for (int i=0; i<lst.Count; i++) {
      ICanDoTheStuff item=myList[i];
      DoSomeStuff(item);
}


Those. for which, in the end, an interface is needed - in order to make a typed list, or some field in the class, and bypass the prohibition on adding (to the list or field) some left garbage there.



The interface is "bureaucracy". Not everywhere it is and not everywhere it is needed, although yes, it is necessary and useful in large projects.



... in general, something like that ... I apologize for the harsh expressions, for some reason it seems to me that a "dry" presentation of the material would be unsuccessful ...



All Articles