OOPへの道:エンジニアの視点

免責事項



この記事は、この資料を「絶対的なゼロ」から研究するという観点を除いて、物事に対する根本的に新しい見方を暗示していません。





この資料は、IT教育なしでOOPを研究する私の道が始まったばかりの約7年前のメモに基づいています。当時はMATLABがメイン言語でしたが、ずっと後にC#に切り替えました。



私が見つけたOOPの原則の提示は、いくつかのリンゴ、「フルーツ」クラスから継承された梨、および一連の用語(継承、多形性、カプセル化など)の形で例を挙げて、中国語の文字として認識されました。



それどころか、今はどういうわけか私はそのような資料を普通に認識しており、私自身の記事からのプレゼンテーションは時々混乱して長く見える。



しかし、私の古いメモとピップボーイのホロディスクに残っている恐ろしいコードは、「古典的な」プレゼンテーションが当時その機能を果たしておらず、完全に失敗したことを示しています。おそらくこれには何かがあります。



これが現実とあなた自身の好みにどれだけ対応するか-あなた自身で決めてください...



OOPの前提条件



ウォールコード



私がMATLAB'eで書き始めたばかりのとき、それが書き方を知っていた唯一の方法でした。私は機能について知っていて、プログラムは部分に分割できることを知っていました。



キャッチは、すべての例が吸い込まれたということでした。誰かのコースブックを開いて、合計2〜3行の小さなボディ機能があったのを見て、これはすべて機能しませんでした(何かが欠けていました)。これは、このゴミを「壁」に組み立て直したときにのみ機能しました。



それから私はいくつかの小さなプログラムを数回書きました、そしてそのたびに私はなぜ共有するものがあるのか​​疑問に思いました。理解が得られたのは後になってからでした。コード「wall」は、約1.5A4ページのプログラムの通常の状態です。機能はなく、神は禁じています。そこではOOPは必要ありません。



これは、Matlabスクリプトがどのように見えるかです(インターネットから取得)。



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)|')


コードを関数に分割する



なぜコードがまだ細かく分割されているのか、そのボリュームが完全に想像を絶するようになり始めたときを推測しました(今、私はアーカイブでたわごとコードを見つけました-壁のそばの650行)。そして、その機能を思い出しました。コードを小さなブロックに分割して、デバッグと再利用を容易にすることができることはわかっていました。



しかし、トリックは異なります-何らかの理由で、すべての教材は変数の関数がどれだけあるかについて沈黙しています...



数学のコースでは、関数はy = f(x)であると言われてい



ます。これは「1つの変数の関数」と呼ばれます。例えば、Y = X 2は全体PARABOLです!

数学の問題:ポイントによってPARABOLを構築します。ノートシート、ボックス。

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









, « », . , , . – . .



-…



画像


そして、関数に4つ以上の変数がある場合…。スーパーストリング理論。Calabi-Yau品種。致命的。与えられていません。理解してください...



要するに、これはすべて間違っています。プログラミングでは、関数の通常の状態は二重膣二重肛門です。100個の変数を受け取り、同じものを返します。これは問題ありません。もう1つ異常なのは、COMMAでそれらをリストすることです。



画像


なんとなく書き方が違うということで、ここで海軍をやっていると気づきました




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


COMMAで区切られた一連の変数。また、呼び出し元のコードには、SelectFun(a、b、c、d…。)のように、これらのパラメーターの名前がまったく異なります。したがって、どの変数がどこにあるかを覚えておく必要があります。そして、COMMAを通じて彼らの手配をします。また、コードが最新化されていて、変数の数が変更された場合は、COMMAを使用してそれらを再配置する必要があります。



そして、なぜこのスカラーにグローバル(シュート!)変数があったのですか?



ビンゴ!COMMAを介してコードをアップグレードするたびに変数を配置しないようにします。



しかし、悪夢のように、COMMAはまだ私をフォローしていました。



画像


そしてバラギンが登場。これは、COMMAを使用して呼び出しコードにさらに多くの引数を追加できることを意味します...



そして、配列について考えました。チュートリアルの例では、配列が次のようになる可能性があるという事実について興奮して話しました。




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


X(2,3)= 6、X(3,3)= 9であることがわかります。このような配列で、行列の乗算を整理できます。前回のレッスンでは、PARABOLS、そして今はMATRIXESを体験しました…。



そして、これらのクソ教科書の1行は短く、明確ではありません。100個の変数の関数を作成し、COMMAを介してリストから外れないようにするには、配列が必要です。



画像


一般的に、私はすべてを1つの大きな2次元テーブルに詰め込むというアイデアを思いつきました。最初はすべてうまくいきました。




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)


しかし、もっと欲しかった。そしてそれはこのように見え始めました:




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)


そして、すべてがうまくいくようですが...ゼロ!異種データを異なる線に分散させたいので、異なるタイプのデータの量が異なっていたために表示されました...そして、関数はこれらのゼロをどのように処理する必要がありますか?コードを更新したい場合はどうなりますか?関数内のこれらの厄介なゼロのハンドラーを書き直す必要があります!結局のところ、いくつかの変数は実際にはゼロに等しいかもしれ



ません...私はこれを決して求めませんでした...



一般的に、これは私がSTRUCTURESについて学んだ方法です。



構造



ここから、データパッケージの方法についてのプレゼンテーションを開始する必要がありました。「テーブル」のある配列は、明らかに歴史的に最初に発生し、それらについても最初に記述します。実際には、「テーブル」としての配列が1次元であるか、まったくないプログラムがたくさんあります。

この構造は、ほぼコンピューターのハードディスク上にデータを「ファイルフォルダー」でパックしたものです。

ドライブD:\

X(変数フォルダー-「オブジェクト」または「構造」)

-a.txt(データを含む変数ファイル-「オブジェクトフィールド」、英語フィールド。番号5

格納されます)-b.txt(番号10が格納されます)

-.txtY

(変数サブフォルダー-「オブジェクト」)

-d.txt(番号2が格納されます)

-e.txt



わかりやすくするために、WindowsExplorerでd.txtファイルへのパスがどのように表示されるかを書き留めておきましょう。

D:\ X \ Y \ d.txt


その後、ファイルを開き、そこに番号「2」を書き込みます。

さて、プログラムコードでどのように見えるか。 「ルートローカルドライブ」を参照する必要がないため、D:\は単に存在せず、ファイル拡張子もありません。残りの部分については、通常、プログラミングでスラッシュ\の代わりにピリオドが使用されます。

次のようになります。




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


matlabでは、チェックアウトを離れることなく、その場で構造(struct)を作成できます。上記のコードは実行可能であり、コンソールにドライブすることができ、すべてがすぐに機能します。構造がすぐに表示され、すべての「変数ファイル」と「変数サブフォルダー」が一度に追加されます。残念ながら、C#についてそう言うことは不可能であり、構造(struct)は痔核によってそこに設定されています。



構造はTABLEARRAYに比べてよりクールで、インデックスの代わりにファイルフォルダシステムです。 Structure = "variable-folder"。これには、 "variables-files"およびその他の "variables-folders"(つまり、一種のサブフォルダー)が含まれます。



すべてがおなじみで、すべてがコンピューター、フォルダー、ファイルとまったく同じです。ファイルには画像はありませんが、数字があります(画像も可能です)。



これは、ARRAY TABLE、特に2次元、そして私を悩ませている3次元以上のテッセラクトを作成するというアイデアと比較して、FUNCTIONに渡すためのデータを格納するためのより高度なバージョンです。

ARRAY TABLEは、次の2つの場合に使用できます。-

小さい(なぜそうなのか?何、コンマで区切られた引数を関数に渡すことができないのか?)。

-ループを作成して検索/入力を自動化することもできます(これが常に可能であるとは限りません)

実際には、ARRAY TABLEは通常、同種のデータの1次元行としてのみ使用されます。通常のプログラムの他のすべては、「ファイルフォルダ」スキームに従って行われます。



では、なぜプログラミング教科書は配列とテーブルで始まるのですか?!!!



要するに、自分で構造を「発見」したので、私は金鉱を見つけたと判断し、すべてを緊急に書き直しました。たわごとのコードは次のようになり始めました:




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)


はい、ここで完璧主義を実行して、ネストされたオブジェクトの束を作成できますが、それは重要ではありません。主なことは、関数内で、変数がその順序番号(COMMAで区切られた引数リスト内にある場合)ではなく、名前によってインデックス付けされることです。そして、ばかげたゼロはありません。そして、関数呼び出しは受け入れ可能な形式になりました。コマンドは2つしかないため、落ち着いて息を吐くことができます。



クラス



「クラス」の概念は、カプセル化、継承、多態性、静的メソッド、フィールド、プロパティ、通常のメソッド、コンストラクターなど、多くの用語を私にもたらしました...#@%!!! ..

経験不足から、構造を理解したので、エンティティを複雑にする必要はないと判断しました。不必要に、そして考えました-「クラスは同じ構造のようですが、より複雑なだけです」。



ある程度そうです。より正確には、これはまさにそれが何であるかです。クラスは、非常に深く見ると、STRUCTURE(テーブルによる配列のイデオロギー的な子孫)であり、プログラムの開始時に作成されます(一般に、起動時だけでなく、そうであるように見えます)。 ARRAY TABLEの子孫と同様に、データはそこに格納されます。プログラムの実行中にアクセスできます。



したがって、私の最初のクラスは次のようなものでした(C#で例を書いていますが、matlabでは、静的フィールドは通常実装されておらず、静的関数の永続変数を持つハックカーブを介してのみ実装されます)。



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

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


上記のケースは、いわば、クラスの「基本的な」スキルであり、愚かなことにデータを含む配列(構造)です。このデータはプログラムの開始時にスローされ、上記の構造からデータを引き出したのとまったく同じ方法で、そこから抽出できます。staticキーワードがこれに使用されます->



構造はどこにでも作成され、いつでも入力されたデータを格納します。



クラス->は、プログラムの起動時に作成される構造です。 staticという単語でマークされたすべてのフィールドは、通常の構造と同様に、単にデータを格納します。静的メソッドは、フォルダーからのように、クラスから呼び出される単純な関数です。




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


私はギャグを持っていました-フィールドが変数でメソッドが関数である場合、それらはどのように1つの場所に保存されますか?私が理解しているように、クラス内の関数(メソッド)は実際には関数ではなく、関数へのポインターです。それら。それは、それを扱うという点では、piとほぼ同じ「変数」です。

要するに、最初はこの巻のクラスを正確に理解し、静的関数のみが使用されるたわごとコードの別の部分を作成しました。それ以外の場合、関数を含むフォルダーとして、クラスをまったく使用しませんでした。



この点は、これがまさにMATLABでクラスが行われる方法であるという事実によっても促進されました-名前が@で始まる(スペースなしの@ Mathのように)そのような愚かなフォルダーのように、その中に.m拡張子を持つ実際のファイルは関数(メソッド)であり、拡張子が.mのヘッダーファイル。これは、CircleLength関数が実際にクラスに属していることを説明するものであり、OOP以外の関数がスローされた単なる.mファイルではありません。

@ Math%フォルダー

-Math.m%ヘッダーファイル

-CircleLength.m%関数ファイル

はい、普通の人が1つの.mファイルにクラスを書くためのより身近な方法がありますが、最初はそれについて知りませんでした。matlabの静的フィールドは一定であり、プログラムの起動時に1回書き込まれます。おそらく、Math.pi = 4を割り当てることを決定する「トロール」から保護するためです(IMHO、まったく役に立たない愚かなトピック、通常の人はmatlabで大きなプロジェクトを作成せず、プログラマーは小さなプロジェクトをデバッグするため、可能性は低いです。彼はばかです)。



しかし、トピックに戻ります。静的メソッドに加えて、クラスにはコンストラクターもあります。コンストラクターは基本的に、y = f(x)またはy = f()のような関数です。入力引数がなく、出力引数が必要であり、これは常に新しい構造(配列)です。



コンストラクターが行うこと。彼はただ構造を作ります。論理的には次のようになります。



C#コード 同等のブール値(疑似コード)


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();	






matlabでコードをたわませ、クラスなしで同様の構造を作成します(クラスが存在する場合-以下を参照):




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


そして出力で私達は構造を持っています

Y(フォルダー変数)

-a(ファイル変数、5に等しい)

-b(ファイル変数は10に等しい)

このことから、実際には、いわゆるクラスフィールド(静的ではなく、静的キーコードなし)がコンストラクター関数内で宣言されたローカル変数であることは明らかです。それらがコンストラクターではなく外部のある種の悪魔のために書かれているという事実はSYNTAXICSUGARです。



SYNTAX SUGAR-プログラミング言語のそのようなでたらめな機能。コードが書かれたときに、コードを難読化するように見え始めます。しかし一方で、それは(おそらく)書かれるより短くそしてより速くなります。



この「発見」をしたことで、当時マトラブでしか書いていなかった私は、信じられないほど驚きました。



matlabでは、上で書いたように、これらの構造は、Ya = 5Yb = 10と書くだけで、コンストラクターなしでその場で作成できます。、オペレーティングシステムのあなたと同じように、キャッシュレジスタを離れることなくファイルやフォルダを作成できます。



そしてここで-ある種の「コンストラクター」、および構造のすべてのフィールド(matlabではプロパティと呼ばれます-プロパティ、厳密に言えば、プロパティはフィールドよりも泥だらけです)は、ヘッダーファイルに官僚的に書き込む必要があります。何のために?このシステムで私が見た唯一の利点は、構造フィールドが事前定義されていることです。これは「自己文書化」のようなものです。何が存在し、何が存在しないかをいつでも確認できます。これが私がその時に書いたこのようなものです:




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


それら。あなたはすべてを正しく理解しました:メソッドは静的であり、xsコンストラクターは何のためのものです(ドキュメントに書かれています-ああ、クラスにはコンストラクターが必要です-まあ、ここにあなたのためのコンストラクターがあります)、私が愚かに知らなかった他のすべては私がZenとOOPを知っていると決めました。



しかし、それにもかかわらず、クラスフォルダーによって関数(静的メソッド)を収集することは私にはクールなアイデアのように思えました。それらはたくさんあり、私はたわごとのコードを書くために座った。



官僚



そして、そのようなことに遭遇しました。いくつかの低レベルのロジックの関数のセットがあります(これらは静的であり、クラスフォルダーにパックされているため、クラスの名前は省略します)。




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


小規模なプロジェクトでは、このような機能の優位性を実現することは不可能です。教育的な例には、一般に2〜3の機能が含まれています。たとえば、「PARABOLを構築する方法を確認する」などです。



そして、ここで-機能のクソクラウド、そしてそれらのそれぞれ、彼らの母親、それぞれが出力引数を持っています、そしてそれらすべてをどうするか?より高い(「主要な」)レベルのロジックの機能を導入してください!通常、それらの数ははるかに少なくなります(通常、20ではなく5)。それら。条件付きで、これらのY1、Y2、Y3….Y20を何らかの方法で取得し、それらをいくつかのZ1、Z2…Z5にリッピングする必要があります。後でパーティーの会議を開くことができるように、そしてそれで:




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


しかし、Z1…Z5はそれだけではありません。それらを作成するには、FUNCTIONS-WRAPPERSが必要です。従来、彼らはこのようなもので動作します...




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


そして、別の「管理」レベルがあるかもしれません...



要するに、私は私がロジスティック地獄にいることに気づきました。通常、小さな関数y = f(x)のFIGURE CLOUDから、別のFIGURE CLOUD再パッケージ化-官僚関数を記述せずにデータを抽出することはできませんでした。データをより高いレベルに転送する場合は、より多くのラッパーが必要です。最終的なプログラムは、徹底的に官僚主義でぎゅうぎゅう詰めになっています-「ビジネスコード」よりも多くのリパッカーがあります。 folder-for-functionsクラスは、この問題を解決しません。それらは、官僚的な馬鹿なリパッカーを山に集めるだけです。



そして、私はこのくだらないコードを近代化することに決めました、そして、官僚的な部分全体を見なければ、これは不可能であることがわかりました!



ロシアでの生活のように...



私は自分が何か間違ったことをしていることに気づき、OOPをよりよく理解しました。そして解決策-このように見れば、それはイデオロギー的に表面上にありました。



OOPのアイデア



1つの引数を作成できるのに、異なる出力引数Y1….Y20生成するy = f(x)のような一連の関数を実行するのはなぜですか種類:




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


次に、関数のすべての結果が1つの構造、1つの配列、異なるコンパートメントにプッシュされます。すべて。次に、Y_allを直接トップ、「リーダーシップ」の上位レベルに転送できます。




Y_all=DO_MOST_IMPORTANT_SHIT(Y_all, options_how_to_do_this_shit)


すべて-すべて-すべての機能-SEALERS-BUREAUERSはお尻に行きます!すべてのデータは1つのY_allベースに収集され、すべての低レベル関数は作業の成果をさまざまなY_allコンパートメントに配置し、「管理」はすべてのY_allコンパートメント駆け巡り、必要な処理を実行します。余分なものは何もありません、コードは迅速に書かれ、うまく機能します...



これはまさにOOPのアイデアであり、構成されています。教科書では、リンゴとナシについての教育例を書き、5行でプログラムを示しています。 5行の例では、OOPはまったく必要ありません。 「トップマネジメントレベル」へのデータ転送は、問題なく直接実行されます。



大規模なプロジェクトが「官僚化」の問題である場合、OOPが必要です...。

しかし、要点に戻ります。実際のOOPには、SYNTAXSUGARがあります。Y_allが構造のみを使用した上記の例では、関数f(、、、)は静的と見なされます。OOPは、コードが次のようになり始めたときの砂糖のセットです。




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);


それら。Y_allを2回書くことはできないが、1回だけ書くという泥だらけの構文を配置することにしました繰り返しのために吃音の母です。



「OOPのしくみ」の残りの説明は、構文糖がどのように機能するかを説明することに要約されます。



OOP構文シュガーのしくみ



まず、このデータベースY_allは、明らかに、関数の引数として使用する前に作成する必要があります。これにはコンストラクターが必要です。



第二に、それがどのような「コンパートメント」を持つかを、できれば事前に予測することをお勧めします。Y_allデータベース小さい限り、この設定は煩わしいものです。「オンザフライで作成されるクラス」について夢見たいと思います。MATLABの場合とほぼ同じ方法で、簡単なコマンドYa = 5Yb = 10で構造を作成できますしかし、健全なプロジェクトをデバッグした後、このトピックについて空想したいという欲求は消えます。



次へ-メソッド(関数)を呼び出します。



これはそれがおおよそ進化した方法です

関数 コメント
Y = f(X) これは、PARABOLをポイントでプロットしたときの数学の場合です。
X = f(X) 私たちは官僚にいじめられました、そして私たちはすべての機会のため1つの議論の1つのバッチを持っており、すべての入力と出力データを内部の異なるコンパートメントに保存しています
f(X) 関数が引数を返す必要があるのはなぜですか?これが数学の授業の時代の古風です!そして、無意味なメモリの浪費!データが参照によって渡されると、関数自体が引数に到達し、変更して終了します。何も= f(X)

山はモハメッドに行くのではなく、モハメッドが山に行く。
X.f() X引数を構文シュガーで引き出しました。NOTHING = X.f(NOTHING)




さて、NOTHINGを受け入れてNOTHING(C#のvoidキーワード)を返すような関数はどのように内部的に配置されていますか。



私はこれがmatlabでどのように行われるかが好きです(理解の観点から):Xf()として呼び出す関数は内部的に次のように記述されています

サンプルMATLABコード サンプルC#コード

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 ( ).





これは、フォルダのようにクラスにある「 デフォルトの引数はthis」の関数です。通常のメソッドがあります(ロシア語で正しくあるため、xs)。

実際には、単一にすべての引数を詰め込み、このことは必ずしも正しくありません。他の引数が必要な場合があります(たとえば、これはユーザー入力です)。




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


voidを 記述せずに、引数を返す必要がある場合もあります(たとえば、操作の成功または失敗について)ただし、これは統計を変更しません。ほとんどのOOP関数はNOTHING(void)を返し、何も(デフォルトの引数はカウントされません)またはごくわずかな引数を受け入れます。 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


今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


私がそれを理解したとき、コードを書くことに関する問題のほとんどが背景に薄れていったように見えました...



プロパティとフィールド



例を見てみましょう。

プロパティなし プロパティ付き

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;


コメント:引数

Set_aは

Set_a(int YourVarName)に関係なく呼び出すことができます。

コメント:

セット{...}内の変数は常に値と呼ばれる必要があります



これは非常に便利でよく使用されますが、それでもSYNTAXSUGARです。

フィールドは完全修飾変数です。プロパティは2つのクラスメソッド(getとset)であり、その呼び出し構文は「変数呼び出し」をコピーします。



実際、getとsetの内部では、がらくたを行うことができます。




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


したがって、名前のプロパティは大文字で、フィールドは小文字で記述することをお勧めします。



プロパティをすばやく実行する必要がある場合(たとえば、インターフェイスにフィールドを作成できない場合)、次のことができます。




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


継承、カプセル化、多形性



なぜあなたはそれらについて前に言及しなかったのですか?なぜなら

、実際、コードを書くとき、「OK Google、OOPとは何ですか」というクエリで言及されているような力で需要がないからです。最初は、彼らは実質的に不必要だとさえ言っていました

-それらが必要な場合、あなたはそれらについて読むことができます(怠惰な人だけがこのケースについて書いていませんでした)。
OOPスタイルのライティングスキルを習得するプロセスがある場合

-継承のないほとんどのクラスがあります。必要なクラスにすべての機能を記述するだけで、実際に何かを継承する必要はありません。

-したがって、多形性(継承へのローション)も森を通り抜けます

-「カプセル化」は、どこでも(すべてのフィールド、プロパティ、およびメソッドに)パブリックを割り当てることになります。

それからあなたの手はあなたの肩に成長します、そしてあなたはこの記事なしであなた自身がそれを理解するでしょう、あなたがこれをするべきではないところ、特にあなたが公に書くべきではないところ。



しかし、それでもそれらの簡単な概要。



継承。これは巧妙なコピー&ペーストです



「継承」の欠陥のある実装は次のようになります。

ああ、私のたわごとコードにはMyClassというクラスがあり、もう1つのSHITフィールドともう1つのDO_THE_SHIT()メソッドがありません。

* Ctrl + C、Ctrl + V

*新しいクラスMyClass_s_fichamiが作成され、目的のクラスがそこに追加されます

それでも、私たちはより文明的な人々であり、プログラムのテキストをコピーするのではなく、それを参照する方がよいことを私たちは知っています。



私たちがまだいくつかの古代のプログラミング言語で書いているか、「継承」のようなことを認識していないとしましょう。次に、2つの異なるクラスを作成します


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




右側で行ったことは継承です。通常のプログラミング言語でのみ、これは1つのコマンドで実行されます:




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;
    }
}


コードは、オプション2とまったく同じように「外部」で機能します。オブジェクトは、いわば「matryoshka」になります。あるオブジェクトの内部に別のオブジェクトが愚かに配置され、「通信チャネル」があり、それを引っ張ると、内部オブジェクトを直接参照できます。



スポイラーヘッダー
image



matlabでは、状況はやや興味深いものです。子コンストラクターMyClassBを実行すると、MyClassAの祖先コンストラクターへの静かな呼び出しはありません



直接作成する必要があります。一方では、これは迷惑です:




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


ただし、子孫がMyClassB(d)などの他の引数を使用して呼び出された場合は、次のように内部で変換を行うことができます。




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


C#では、これを直接行うことはできず、これにより、ある種の「変換関数」を作成する必要が生じます。




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


または、次のような「静的コンストラクタ」を実行します。




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


それは継承、基本的にすべてについてのようです。



当然のことながら、継承者に「F1メソッドaプロパティを強制的に記述させることはないため、これらは必ず祖先のメソッドとフィールドの呼び出しに変換されます。ブロードキャストは、デフォルトの「継承」動作にすぎません。 あなたは(もちろん!これらは別のクラスの他のメソッドです、bro)次のように書くことができます:








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(“”);//     
    }
}


カプセル化



...概念的には、これは、ベースフィールドのクラスMyClassBのオブジェクトの内部に、クラスMyClassAのオブジェクトがあり、制御コマンドを外部にブロードキャストできることを意味します。これはすべて上に書かれており、繰り返す意味がありません。



-そこなトピックは、異なるアクセス修飾子であり、パブリックプライベート保護された...それらについて、最も興味深いのは何ですが、通常は、多かれ少なかれどこでも書かれている、私はそれについて読んでお勧めします。

public- これは、フィールドプロパティ、またはメソッドが外部から表示され、プルできることを意味します。

何をすべきかわからない場合は、公開してください(悪いアドバイス、はい)。



次に、自分の強みを見つけて、不要な場所にこのパブリックを破棄します(または、明確にするために、プライベートに置き換えます)(「リファクタリング」を実行します)。はい、もちろん、先見の明があり、精神病の戦いで行動し、どこをプライベートにするかをすぐに推測することは非常に良いことです

private-これは、ファイルフォルダオブジェクトフィールドプロパティ、またはメソッドが、このクラスのメソッド内からのみ表示されることを意味します。

しかし...正確には、INSTANCE(オブジェクト)ではなくクラスです。次のようなコードがある場合:




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


そのようなものはクローニングで使用されます(詳細については他のソースを参照してください)。



コードを書くときはこのパブリックプライベートの配置について考えようとしました。これは、コードを大まかに作成するときに、許容できないほど時間のかかるコードです。そして、コード自体は根本的に異なる方法で実行する必要があることがわかりました。



コードが単独で書かれている場合、事前にプライベートパブリックを気にすることは意味がありません。たとえば、実際にコードを考えて書くなど、より重要なタスクがあります...

プライベートとパブリックを配置する場所が多かれ少なかれ明確である唯一の場所は同じですある種のフィールドを参照する悪名高いプロパティ。




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


他の場所では、公的および私的を手配するために、あなたはプログラムが何をしているのかを実際に見る必要があり、おそらくこれを「不在で」学ぶことはうまくいきません。

保護-これは、派生クラスのすべてのメソッドに対してパブリック」を意味しその他すべてに対してプライベート」を意味します。

一般に、継承されたクラスがそれらの祖先の単なる「より洗練されたバージョン」として表示されると仮定するのは論理的です。



正直なところ、この保護を明示的に適用した場所をすでに忘れています。通常、パブリックまたはプライベートのいずれか。私が書いたクラスのほとんどは、他のカスタムクラスから継承していませんでした。継承した場合、そのようなものが深刻に必要になることはめったにありませんでした。



印象は、多くの人々によってサポートされているかもしれないいくつかの大規模なプロジェクトに取り組んでいるときに非公開の修飾子が要求されているということです... 「通信で」勉強するとき、どういうわけかこの理解を与えることは難しいです。



多形性



Matlabで書いていたとき、なぜ多形性が必要なのか、それが何であるのか理解できませんでした。

その後、C#に切り替えたところ、これが厳密に典型的な言語の特徴であり、OOPとの関係が非常に弱いことに気づきました。 matlabでは、この多型の存在を知らなくてもどこにでも書くことができます。厳密な型指定はありません。



簡単にするために、クラスをAおよびBと呼びます。




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


これはタイプキャスティングと呼ばれます。C#では、(方法を知っている場合は)自分でカスタムの自作タイプのキャスティングシステムを作成できます。

ここでは、箱から出して「キャスト」するだけです。クラスAの別のオブジェクトがクラスB属するオブジェクトxの内部にあるため、一見明らかなキャスト方法の1つは、外部オブジェクトから内部オブジェクトへのすべての接続を閉じることです。 これは実際には必要ではありませんが、「多形性」を発明した人々は、そうすることが最も明白であると判断しました。そして、ユーザーは残りのオプションを自分で作成します。 2008〜 2012年のサンプルの(関連性がなくなった)「politota」については申し訳ありません。










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


インターフェース



これを適用する方法から始めなければなりません。



リストがあり、そこに何かを入れたいとしましょう。



matlabでは、これを行う最も簡単な方法は(セル配列と呼ばれます)です。




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


あなたはそれがどんな種類のオブジェクトであるかを考えません、あなたはそれを取り、それをリストに載せるだけです。



次に、リストをループして、各要素で何かを行う必要があるとします。




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


DoSomeStuff関数が、供給されたものをすべて消化するのに十分スマートである場合、このコードは実行されます。



DoSomeStuff関数(またはその作成者)がインテリジェンスで輝いていない場合は、数字、線、自作のクラス、禿げた悪魔、または-神は禁じられている-あなたの祖母など、何かを窒息させる可能性があります。



MATLABは、コンソールに英語で赤い誓いを表示し、プログラムを終了します。したがって、コードは自動的にダーウィン賞を受賞します。



ただし、コードが非常に複雑な場合があるため、これは実際には悪いことです。そうすれば、すべてが正しく行われたことを確信できますが、実際には、アクションの誤った組み合わせがテスト中に開始されることはありませんでした。



そのため、MATLABでは(理由だけではありませんが)、コードのひどいサイズで、これを自分で確認することができました(KPDVとほぼ同じです)。大きなプロジェクトを作成する必要はありません。



それでは、C#に移りましょう。リストを作成し、...そしてオブジェクトのタイプをすぐに示すように求められます。リストタイプのリストを作成します。



そのようなリストには番号1を入れることができます。



そのようなリストには番号2を入れることができ、さらには神は私を許してくれます、3。




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


しかし、テキスト文字列はもうありません。自作のクラスのオブジェクト-厳密にはそうではありません。私は禿げた悪魔とあなたの祖母について沈黙しています、彼らはどんな変種の下でもそこにいることはできません。



行の個別のリストを作成できます。あなたは-あなたの自作のクラスのために-することができます。




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


実際、祖母であるBaldDevilsのリストを個別に作成できます。



ただし、それらを1つのリストに追加しても機能しません。あなたのコードは、あなたがそれを実行しようとする前にコンパイラの乱用と組み合わされて、ダーウィン賞を獲得します。コンパイラは慎重にDoSomeStuff(アイテム)関数を作成することを許可していません。これはその引数で「チョーク」します。



これは大規模なプロジェクトで非常に便利です。

しかし、それでも1つの小さなリストに入れたい場合は、どうすればよいでしょうか。



これは実際には問題ではありません。すべてをタイプオブジェクトに変換するだけで十分です。ほとんど(または絶対に)すべてをタイプオブジェクトに変換できます




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


リストをループし始めると、問題が始まります。重要なのは、オブジェクトタイプは(ほとんど)何もできないということです。オブジェクトタイプのBEのみです。

-何ができますか?

-歌ったり踊ったり できます

-そして私-三丁...-

何ができますか、三丁?

-私は三丁です。

-さて、何かできますか?

- 理解していない。私は三丁になれます。



したがって、インターフェイスが作成されます。これは継承元のクラスです。インターフェイスには、メソッドヘッダーとプロパティヘッダーが含まれています。



この場合、これらはDoSomeStuff(アイテム)関数のNORMAL操作を保証するメソッドとプロパティです。インターフェイスは、プロパティ自体を実装していません。これは意図的に行われます。実際、DoSomeStuff()関数での使用に適したクラスから継承することもできます。しかし、それは余分なコードと忘れられたプログラマーを意味します。



したがって、仲間のプログラマーがインターフェースから継承したが、クラスの必要なプロパティとメソッドを実装するのを忘れた場合、コンパイラーはそれをダーウィン賞でコードに書き込みます。したがって、これを行うことができます:




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);
}


それら。そのためには、最終的にはインターフェイスが必要です-型付きリストまたはクラス内のフィールドを作成し、そこにある種の残されたゴミを(リストまたはフィールドに)追加することの禁止を回避するためです。



インターフェースは「官僚主義」です。どこにでもあるわけではなく、必要な場所でもありませんが、大規模なプロジェクトでは必要であり、有用です。



...一般的には...そのようなもの...厳しい表現をお詫びします。何らかの理由で、資料の「乾いた」提示は失敗するように思われます...



All Articles