Jalan Menuju OOP: Perspektif Seorang Insinyur

Penolakan



Artikel tersebut tidak menyiratkan pandangan fundamental baru tentang berbagai hal, kecuali dari sudut pandang mempelajari materi ini dari "nol mutlak".





Materi ini berdasarkan catatan sekitar 7 tahun yang lalu, ketika jalur saya dalam belajar OOP tanpa pendidikan IT baru saja dimulai. Di masa itu MATLAB adalah bahasa utama, lama kemudian saya beralih ke C #.



Presentasi prinsip-prinsip OOP, yang saya temukan, dengan contoh berupa beberapa apel, pir yang diwarisi dari kelas "buah" dan sekumpulan terminologi (pewarisan, polimorfisme, enkapsulasi, dll.), Dianggap sebagai huruf Cina.



Sebaliknya, sekarang untuk beberapa alasan saya menganggap materi seperti itu secara normal, dan presentasi dari artikel saya sendiri terkadang tampak membingungkan dan panjang.



Tetapi catatan lama saya dan kode mengerikan yang masih ada pada holodisk di pipboy menunjukkan bahwa presentasi "klasik" tidak memenuhi fungsinya pada saat itu, dan sama sekali tidak berhasil. Mungkin ada sesuatu di sini.



Seberapa sesuai dengan kenyataan dan preferensi Anda sendiri - putuskan sendiri ...



Prasyarat untuk OOP



Kode dinding



Ketika saya baru mulai menulis di MATLAB'e, itu adalah satu-satunya cara untuk menulis dan tahu caranya. Saya tahu tentang fungsi dan program dapat dibagi menjadi beberapa bagian.



Hasil tangkapannya adalah bahwa semua contoh tersedot. Saya membuka buku kursus seseorang, melihat ada fungsi tubuh kecil 2-3 baris, total semua ini TIDAK berfungsi (ada yang hilang), dan hanya berfungsi ketika saya memasang kembali sampah ini menjadi "dinding".



Kemudian saya menulis beberapa program kecil beberapa kali, dan setiap kali saya bertanya-tanya mengapa ada sesuatu untuk dibagikan. Baru kemudian pemahaman datang: kode "dinding" adalah keadaan normal program sekitar 1,5 halaman A4. Tidak ada fungsi dan, Tuhan melarang, OOP TIDAK diperlukan di sana.



Seperti inilah tampilan skrip Matlab (diambil dari 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)|')


Membagi kode menjadi beberapa fungsi



Mengapa kode masih dibagi menjadi beberapa bagian, saya menebak kapan volumenya mulai menjadi benar-benar tak terbayangkan (sekarang saya menemukan kode sial di arsip - 650 baris di dinding). Dan kemudian saya ingat tentang fungsinya. Saya tahu bahwa mereka memungkinkan Anda untuk membagi kode Anda menjadi blok-blok kecil yang lebih mudah untuk di-debug dan digunakan kembali.



Tetapi triknya berbeda - untuk beberapa alasan semua bahan ajar diam tentang BERAPA BANYAK fungsi variabel ...



Mata kuliah matematika mengatakan bahwa suatu fungsi adalah y = f (x)



Ini disebut "fungsi satu variabel." Misalnya, y = x 2 adalah PARABOL utuh!

Soal matematika: buat PARABOL dengan poin. Di lembar buku catatan, di dalam kotak.

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









, « », . , , . – . .



-…



gambar


Dan jika fungsi memiliki empat variabel atau lebih…. Teori superstring. Varietas Calabi-Yau. Makhluk hidup. Tidak diberikan. Pahami ...



Singkatnya, ini semua salah. Dalam pemrograman, keadaan normal suatu fungsi adalah vagina ganda anal ganda . Dibutuhkan 100 variabel dan mengembalikan yang sama, itu bagus. Hal lain yang tidak normal - mencantumkannya dengan COMMA.



gambar


Tentang fakta bahwa Anda dapat menulis entah bagaimana berbeda, saya menyadari ketika saya angkatan laut DI SINI INI




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


Sekelompok variabel yang dipisahkan oleh COMMA. Dan kode panggilan memiliki nama yang sangat berbeda untuk parameter ini, seperti SelectFun (a, b, c, d….) Oleh karena itu, Anda perlu mengingat di mana variabel itu. Dan buat pengaturannya melalui COMMA. Dan jika kode sedang dimodernisasi, dan jumlah variabel berubah, maka mereka harus diatur ulang lagi dengan COMMA.



Dan mengapa variabel global (tembak!) Dalam kemelaratan ini?



Bingo! Agar tidak mengatur variabel dengan setiap peningkatan kode melalui COMMA.



Tetapi COMMA masih mengikuti saya seperti dalam mimpi buruk.



gambar


Dan varargin muncul. Ini berarti bahwa saya dapat menambahkan lebih banyak argumen dalam kode panggilan, dipisahkan oleh COMMA ...



Dan kemudian saya memikirkan tentang array. Contoh tutorial dengan bersemangat berbicara tentang fakta bahwa array bisa seperti ini:




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


Dan Anda lihat, X (2,3) = 6, dan X (3,3) = 9, dan kita ... kita dapat mengatur perkalian matriks pada array seperti itu! Di pelajaran terakhir kita membahas PARABOL, dan sekarang MATRIKS….



Dan tidak ada satu baris pun dari buku teks sialan ini yang pendek dan jelas: array diperlukan untuk membuat fungsi dari 100 variabel dan tidak jatuh dari daftar mereka melalui COMMA.



gambar


Secara umum, saya mendapat ide untuk menjejalkan semuanya ke dalam satu tabel dua dimensi yang besar. Semuanya berjalan dengan baik pada awalnya:




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)


Tapi saya menginginkan lebih. Dan itu mulai terlihat seperti ini:




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)


Dan semuanya tampak baik-baik saja, tapi ... NOL! Mereka muncul karena saya ingin menyebarkan data heterogen ke jalur yang berbeda, dan jumlah data dari jenis yang berbeda berbeda ... Dan bagaimana seharusnya fungsi memproses angka nol ini? Apa yang terjadi jika saya ingin memperbarui kode? Saya harus menulis ulang handler untuk angka nol yang menjijikkan ini di dalam fungsi! Bagaimanapun, beberapa variabel sebenarnya bisa sama dengan nol ...



Saya tidak pernah meminta ini ...



Secara umum, ini adalah cara saya belajar tentang STRUKTUR.



Struktur



Di sinilah perlu untuk memulai presentasi tentang metode pengemasan data. Array dengan "tabel", tampaknya, secara historis muncul pertama kali, dan mereka menulis tentang mereka juga - di awal. Dalam praktiknya, Anda dapat menemukan banyak program di mana array sebagai "tabel" berbentuk satu dimensi, atau tidak ada sama sekali.

Strukturnya adalah "file-folder" yang mengemas data, kira-kira pada hard disk komputer.

Drive D: \

X (variabel-folder - "objek" atau "struktur")

- a.txt (file variabel dengan data - "kolom objek",

kolom bahasa Inggris. Nomor 5 disimpan) - b.txt (nomor 10 disimpan )

- .txt

Y (variabel-subfolder - "objek")

- d.txt (nomor 2 disimpan)

- e.txt



Untuk membuatnya lebih jelas, mari tuliskan bagaimana kita akan melihat jalur ke file d.txt di Windows Explorer

D: \ X \ Y \ d.txt


Setelah itu kita buka file tersebut dan tulis angka "2" disana.

Sekarang - bagaimana tampilannya di kode program. Tidak perlu merujuk ke "root drive lokal", jadi D: \ tidak ada di sana, kami juga tidak akan memiliki ekstensi file. Sedangkan sisanya, titik biasanya digunakan sebagai pengganti garis miring \ dalam pemrograman.

Ternyata seperti ini:




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


Di matlab, struktur ( struct ) dapat dibuat langsung di tempat, tanpa keluar dari kasir, mis. kode di atas dapat dieksekusi, dapat didorong ke konsol dan semuanya akan langsung berfungsi. Struktur akan segera muncul, dan semua "file-variabel" dan "subfolder variabel" akan ditambahkan ke sana sekaligus. Sayangnya, tidak mungkin untuk mengatakan bahwa tentang C #, struktur ( struct ) diatur oleh wasir.



Strukturnya adalah relatif lebih dingin dari ARRAY OF TABLES, di mana alih-alih indeks - sistem file-folder. Structure = "variable-folder", yang berisi "variable-files" dan "variable-folder" lainnya (mis., Semacam subfolder).



Semuanya akrab, semuanya persis sama seperti di komputer, folder, file di dalamnya, hanya di file bukan gambar, tetapi angka (meskipun gambar juga dimungkinkan).



Ini adalah versi penyimpanan data yang lebih canggih untuk diteruskan ke FUNCTION dibandingkan dengan ide membuat TABEL ARRAY, terutama dua dimensi, dan, mengganggu saya, Tesseract, tiga dimensi dan lebih banyak.

TABEL ARRAY dapat digunakan dalam dua kasus:

- itu kecil (mengapa kemudian? Apa, Anda tidak dapat meneruskan argumen yang dipisahkan oleh koma ke fungsi?).

- Anda dapat membuat loop di atasnya dan mengotomatiskan pencarian / pengisian (ini tidak selalu memungkinkan)

Pada kenyataannya, ARRAY TABLE biasanya hanya digunakan sebagai baris satu dimensi dari data homogen. Segala sesuatu yang lain dalam program normal dilakukan sesuai dengan skema "file-folder".



Lalu mengapa buku teks pemrograman dimulai dengan array dan tabel? !!!



Singkatnya, "setelah menemukan" struktur untuk diri saya sendiri, saya memutuskan bahwa saya telah menemukan tambang emas dan segera menulis ulang semuanya. Kode buruknya terlihat seperti ini:




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)


Ya, Anda dapat melakukan perfeksionisme di sini dan membuat banyak objek bersarang, tetapi bukan itu intinya. Hal utama adalah bahwa sekarang, di dalam fungsi, variabel tidak diindeks oleh nomor urutnya (di mana ia ada dalam daftar argumen, dipisahkan oleh COMMA), tetapi dengan nama. Dan tidak ada angka nol yang bodoh. Dan panggilan fungsi sekarang dalam bentuk yang dapat diterima, hanya ada 2 PERINTAH, Anda dapat bernapas dengan tenang.



Kelas



Konsep "kelas" menurunkan banyak terminologi pada saya: enkapsulasi, pewarisan, polimorfisme, metode statis, bidang, properti, metode biasa, konstruktor ... # @% !!! ..

Karena kurang pengalaman, setelah mengetahui struktur, saya memutuskan bahwa tidak perlu mempersulit entitas yang tidak perlu, dan pikiran - "kelas seperti struktur yang sama, hanya lebih rumit."



Sampai batas tertentu, memang demikian. Lebih tepatnya, inilah tepatnya. Sebuah kelas, jika Anda melihat lebih dalam, adalah STRUKTUR (keturunan ideologis dari sebuah array dengan tabel), yang dibuat saat program DIMULAI (secara umum, tampaknya, dan tidak hanya saat startup). Seperti pada turunan dari TABLE ARRAY, data disimpan di sana. Mereka dapat diakses saat program sedang berjalan.



Oleh karena itu, kelas pertama saya adalah seperti ini (saya menulis contoh di C #, di matlab, bidang statis biasanya tidak diterapkan, hanya melalui kurva peretasan dengan variabel persisten dalam fungsi statis).



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

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


Kasus di atas adalah, seolah-olah, keterampilan "dasar" dari sebuah kelas - menjadi sebuah array (struktur) dengan data yang bodoh. Data ini dimasukkan ke dalamnya pada awal program, dan dari sana mereka dapat diekstraksi, dengan cara yang persis sama seperti yang kita tarik keluar dari struktur di atas. Kata kunci statis digunakan untuk ini .



Struktur -> dibuat di mana saja dan menyimpan data yang dimasukkan ke dalamnya setiap saat



Kelas -> adalah struktur yang dibuat saat program dimulai. Semua bidang yang ditandai dengan kata statis hanya menyimpan data, seperti dalam struktur normal. Metode statis hanyalah fungsi yang dipanggil dari kelas, seperti dari folder.




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


Saya punya lelucon - jika bidang adalah variabel dan metode adalah fungsi, lalu bagaimana mereka disimpan di satu tempat? Seperti yang saya pahami, fungsi (metode) di kelas sebenarnya bukan fungsi, tetapi penunjuk ke suatu fungsi. Itu. itu adalah tentang "variabel" yang sama dengan pi dalam hal bekerja dengannya.

Singkatnya, pada awalnya saya memahami kelas-kelas persis di volume ini dan menulis bagian lain dari kode kotoran, di mana HANYA fungsi statis digunakan. Jika tidak, sebagai folder dengan fungsi, saya tidak menggunakan kelas sama sekali.



Poin ini juga difasilitasi oleh fakta bahwa ini persis bagaimana kelas dilakukan di MATLAB - seperti folder bodoh, yang namanya dimulai dengan @ (seperti @ Matematika, tanpa spasi), di dalamnya, file aktual dengan ekstensi .m adalah fungsi (metode) dan ada header file dengan ekstensi .m, yang menjelaskan bahwa fungsi CircleLength benar-benar milik kelas, dan bukan hanya file .m dengan fungsi non-OOP dilemparkan di sana.

@ Folder% Matematika

- File header

Math.m% - File fungsi CircleLength.m%

Ya, ada cara yang lebih familiar bagi orang normal untuk menulis kelas dalam satu file .m, tapi pada awalnya saya tidak mengetahuinya. Bidang statis di matlab hanya konstan, dan ditulis sekali saat program dimulai. Mungkin untuk melindungi dari "trawl", yang memutuskan untuk menetapkan Math.pi = 4 (IMHO, topik yang benar-benar tidak berguna dan bodoh, tidak ada orang biasa yang akan menulis proyek besar di matlab, dan programmer akan men-debug proyek kecil dan karenanya, kecil kemungkinannya dia bodoh).



Tapi kembali ke topik. Selain metode statis, kelas juga memiliki konstruktor. Konstruktor pada dasarnya hanyalah fungsi seperti y = f (x) atau bahkan y = f (). Ini mungkin tidak memiliki argumen input, itu harus memiliki output, dan ini selalu merupakan struktur baru (array).



Apa yang dilakukan oleh konstruktor. Dia hanya membuat struktur. Logikanya terlihat seperti ini:



Kode C # Perkiraan Setara Boolean (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();	






Kode sial di matlab, membuat struktur serupa tanpa kelas apa pun (di mana kelas tersebut ada - lihat di bawah):




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


Dan pada keluaran kita memiliki struktur

Y (variabel folder)

- a (variabel file, sama dengan 5)

- b (variabel file sama dengan 10)

Dari sini, sebenarnya, jelas bahwa yang disebut bidang kelas (bukan statis, tanpa kode kunci statis ) adalah variabel lokal yang dideklarasikan di dalam fungsi konstruktor. Fakta bahwa mereka ditulis untuk beberapa jenis iblis tidak dalam konstruktor, tetapi di luar, adalah GULA SINTAKSIK.



SYNTAX SUGAR - fitur omong kosong seperti itu dari bahasa pemrograman ketika kode mulai terlihat seolah-olah ingin mengaburkannya tepat saat ditulis. Tapi di sisi lain, itu menjadi lebih pendek dan lebih cepat (seharusnya) ditulis.



Setelah membuat "penemuan" ini, saya, yang pada waktu itu hanya menulis di Matlab, sangat terkejut.



Di matlab, seperti yang saya tulis di atas, struktur ini bisa dibuat di tempat, tanpa ada konstruktor, cukup dengan menulis Ya = 5 , Yb = 10, sama seperti Anda di sistem operasi bisa membuat file dan folder tanpa keluar dari mesin kasir.



Dan di sini - semacam "konstruktor", dan semua bidang struktur (di matlab disebut properti - properti, walaupun sebenarnya, properti adalah hal yang lebih tidak jelas daripada bidang) perlu ditulis secara birokratis di file header. Untuk apa? Satu-satunya manfaat yang kemudian saya lihat dalam sistem ini adalah bahwa bidang struktur telah ditentukan sebelumnya, dan ini seperti "dokumentasi mandiri" - Anda selalu dapat melihat apa yang seharusnya ada dan apa yang tidak boleh ada. Inilah yang saya tulis kemudian:




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


Itu. Anda memahami semuanya dengan benar: metodenya hanya statis, konstruktor xs adalah untuk apa (tertulis dalam dokumentasi - Oh, kelas harus memiliki konstruktor - nah, ini konstruktor untuk Anda), saya dengan bodohnya tidak mengetahui yang lainnya dan memutuskan bahwa saya telah mempelajari Zen dan OOP.



Namun demikian, menurut saya ide yang bagus untuk mengumpulkan fungsi (metode statis) oleh kelas-folder. ada banyak sekali, dan saya duduk untuk menulis kode sial.



Birokrasi



Dan bertemu hal seperti itu. Ada sekumpulan fungsi dari beberapa tingkat logika yang lebih rendah (mereka statis dan dikemas ke dalam folder kelas, sekarang kita akan menghilangkan nama kelas):




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


Dalam proyek kecil, tidak mungkin untuk mencapai dominasi fungsi seperti itu, contoh pendidikan biasanya berisi 2-3 fungsi - seperti "lihat bagaimana kita dapat membangun PARABOL".



Dan di sini - sekumpulan fungsi, dan masing-masing, ibunya, masing-masing memiliki argumen keluaran, dan apa yang harus dilakukan dengan semuanya? Letakkan fungsi logika yang lebih tinggi ("memimpin")! Biasanya jumlahnya jauh lebih sedikit (secara konvensional, 5 bukannya 20). Itu. secara konvensional, Anda harus mengambil Y1, Y2, Y3… .Y20 ini dan HAPUS mereka menjadi beberapa Z1, Z2… Z5. Sehingga nanti Anda bisa membuat pertemuan pesta dan di situ:




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


Tapi Z1… Z5 tidak datang dengan sendirinya. Untuk membuatnya, Anda membutuhkan FUNCTIONS-PACKERS. Biasanya, mereka mengerjakan sesuatu seperti ini ...




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


Dan kemudian mungkin ada tingkat "manajemen" lain ...



Singkatnya, saya menyadari bahwa saya berada di neraka logistik. Saya biasanya tidak bisa mengekstrak data dari CLOUD GAMBAR fungsi kecil y = f (x) tanpa menulis GAGAM CLOUD fungsi repackaging-birokrasi, dan ketika data ditransfer ke tingkat yang lebih tinggi, kita membutuhkan lebih banyak REPLACER. Program terakhir dipenuhi dengan birokrasi terus menerus - ada lebih banyak repackers daripada "kode bisnis". Kelas folder-untuk-fungsi tidak memecahkan masalah ini - mereka hanya mengumpulkan repackers idiot birokrasi di tumpukan.



Dan kemudian saya memutuskan untuk memodernisasi kode buruk ini, dan ternyata tanpa melihat seluruh bagian birokrasi, ini tidak mungkin!



Sama seperti kehidupan di Rusia ...



Saya menyadari bahwa saya melakukan sesuatu yang salah, dan memahami OOP dengan lebih baik. Dan solusinya - jika Anda melihatnya dengan cara ini, secara ideologis sudah ada di permukaan.



Ide OOP



Mengapa sekelompok fungsi seperti y = f (x) yang menghasilkan argumen output yang BERBEDA Y1… .Y20 , ketika Anda dapat membuat SATU argumen. Jenis dari:




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


Kemudian secara mutlak semua hasil fungsi akan didorong ke dalam satu struktur, menjadi satu larik, hanya ke dalam kompartemennya yang berbeda. Semua. Kemudian Y_all dapat ditransfer langsung ke atas, ke tingkat atas dari "manajemen".




Y_all=DO_MOST_IMPORTANT_SHIT(Y_all, options_how_to_do_this_shit)


Semua-semua-semua fungsi-SEALER-BUREAUERS pergi ke pantat! Semua data dikumpulkan dalam basis SATU Y_semua , semua fungsi tingkat rendah menempatkan hasil kerja mereka di kompartemen Y_all yang berbeda, pemburu "manajemen" melalui semua kompartemen Y_all dan melakukan apa yang seharusnya dilakukan. Tidak ada yang berlebihan, kodenya ditulis dengan cepat dan berfungsi dengan baik ...



Ini adalah ide dari OOP dan terdiri. Di buku teks, mereka menulis contoh pendidikan tentang apel dan pir, dan kemudian menunjukkan program dalam 5 baris. Tidak perlu OOP sama sekali, dalam contoh untuk 5 baris, sejak transfer data ke "level manajemen puncak" dilakukan secara langsung tanpa masalah.



OOP diperlukan bila proyek besar adalah masalah "birokratisasi" ....

Tapi kembali ke titik. Dalam OOP sebenarnya, ada GULA SINTAKS. Contoh di atas dengan Y_all hanya menggunakan struktur, fungsi f (,,,) akan dianggap statis. OOP adalah sekumpulan gula ketika kode mulai terlihat seperti ini:




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


Itu. kami semacam memutuskan untuk membawa sintaks berlumpur di mana Anda tidak dapat menulis Y_all 2 kali, tetapi melakukannya hanya 1 kali. Karena pengulangan adalah ibu dari kegagapan.



Sisa penjelasan "bagaimana OOP bekerja" bermuara pada penjelasan bagaimana gula sintaksis bekerja.



Bagaimana OOP Gula Sintaksis Bekerja



Pertama, database Y_all ini , tentunya, perlu dibuat sebelum digunakan sebagai argumen ke fungsi. Ini membutuhkan konstruktor.



Kedua, disarankan untuk meramalkan, lebih baik sebelumnya, apa "kompartemen" yang akan dimilikinya. Selama database Y_all kecil, pengaturan ini mengganggu. Saya ingin bermimpi tentang "kelas yang dibuat dengan cepat", dengan cara yang sama seperti di MATLAB Anda dapat membuat struktur dengan perintah sederhana Ya = 5 , Yb = 10 . Tetapi keinginan untuk berfantasi tentang topik ini menghilang setelah men-debug proyek yang sehat.



Berikutnya - memanggil metode (fungsi).



Begitulah kira-kira berkembangnya

Fungsi Komentar
Y = f (X) Ini adalah kasus dalam matematika ketika kami menggambar PARABOL dengan poin!
X = f (X) Kami diintimidasi oleh birokrat, dan kami memiliki satu kumpulan argumen untuk semua kesempatan, menyimpan semua data input dan output di kompartemen berbeda di dalamnya
f (X) Mengapa suatu fungsi mengembalikan argumen? Inilah arkaisme zaman pelajaran matematika! Dan pemborosan memori yang tidak ada gunanya! Biarkan data dilewatkan dengan referensi, maka fungsi itu sendiri akan datang ke argumen, berubah dan pergi. NOTHING = f (X)

Bukan gunung yang pergi ke Muhamad, tapi Muhamad ke gunung.
X.f () Kami baru saja mengeluarkan argumen X dengan gula sintaksis. NOTHING = X.f (NOTHING)




Sekarang - bagaimana fungsi seperti itu diatur secara internal yang tidak mengambil apa-apa dan mengembalikan NOTHING (kata kunci void di C #).



Saya suka bagaimana hal itu dilakukan di matlab (dari sudut pandang pemahaman): fungsi yang kami sebut sebagai Xf () ditulis secara internal sebagai

Contoh Kode MATLAB Contoh Kode 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 ( ).





Berikut adalah fungsi dengan "argumen default ini ", terletak di kelas, seperti dalam folder - ada metode biasa (xs, seperti yang benar dalam bahasa Rusia).

Faktanya, menjejalkan semua argumen menjadi satu hal ini tidak selalu benar. Terkadang Anda membutuhkan beberapa argumen lain (misalnya, ini adalah input pengguna):




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


Terkadang Anda bahkan perlu mengembalikan argumen (misalnya, tentang keberhasilan atau kegagalan suatu operasi), dan tidak menulis void . Namun, yang tidak mengubah statistik: sebagian besar fungsi OOP mengembalikan NOTHING ( void ) dan tidak menerima apa pun (argumen default tidak dihitung) atau sangat sedikit argumen.



Mari tulis kode terakhir

di 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


Sekarang di 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


Ketika saya mengetahuinya, sepertinya sebagian besar masalah dengan penulisan kode memudar ke latar belakang ...



Properti vs bidang



Mari kita lihat contohnya.

tanpa properti dengan properti

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;


komentar: argumen

Set_a bisa disebut apapun

Set_a (int YourVarName)

komentar: variabel di dalam

set {...} harus selalu disebut nilai



Benda ini cukup nyaman dan sering digunakan, namun tetap SYNTAX GULA.

Bidang adalah variabel yang sepenuhnya memenuhi syarat. Properti adalah 2 metode kelas (dapatkan dan set), sintaks panggilan yang menyalin "panggilan variabel".



Faktanya, di dalam get dan set, Anda bisa melakukan omong kosong:




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


Oleh karena itu, sepertinya disarankan untuk menuliskan nama properti dengan huruf kapital, dan field dengan huruf kecil.



Ini terjadi (misalnya, Anda tidak dapat membuat bidang di antarmuka) bahwa Anda perlu melakukan properti dengan cepat, maka Anda dapat:




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


Pewarisan, enkapsulasi, polimorfisme



Mengapa Anda tidak menyebutkannya sebelumnya? Karena

- pada kenyataannya, saat menulis kode, mereka tidak diminta dengan kekuatan seperti yang disebutkan dalam kueri "Ok Google, apa itu OOP". Saya bahkan akan mengatakan bahwa pada awalnya mereka praktis tidak perlu .

- di mana mereka dibutuhkan, Anda dapat membaca tentang mereka (hanya pemalas yang tidak menulis tentang kasus ini).
Saat ada proses penguasaan keterampilan menulis ala OOP

- Anda akan memiliki sebagian besar kelas TANPA warisan. Anda cukup menulis SEMUA fungsionalitas di kelas yang diperlukan, dan Anda tidak perlu mewarisi sesuatu.

- Oleh karena itu, polimorfisme (lotion warisan) juga melewati hutan

- "enkapsulasi" dikurangi untuk menetapkan publik di mana-mana (ke semua bidang, properti dan metode).

Kemudian tangan Anda akan tumbuh ke bahu Anda, dan Anda akan mengetahuinya sendiri, tanpa artikel ini , di mana Anda TIDAK boleh melakukan ini, terutama di mana Anda TIDAK boleh menulis publik.



Tapi masih gambaran singkat tentang mereka.



Warisan. Ini adalah salin-tempel yang cerdas



Penerapan "inheritance" yang cacat terlihat seperti ini:

Oh, kode sial saya memiliki kelas yang disebut MyClass, dan kehilangan satu bidang SHIT lagi dan metode DO_THE_SHIT () lainnya!

* Ctrl + C, Ctrl + V

* Kelas baru MyClass_s_fichami dibuat dan yang diinginkan ditambahkan di sana

Namun, kami adalah orang-orang yang lebih beradab, dan kami tahu bahwa lebih baik tidak menyalin teks program, tetapi merujuknya.



Katakanlah kita masih menulis dalam beberapa bahasa pemrograman kuno atau tidak mengetahui yang namanya "warisan". Kemudian kami menulis 2 kelas yang berbeda


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




Apa yang kami lakukan di sebelah kanan adalah warisan. Hanya dalam bahasa pemrograman normal ini dilakukan dengan satu perintah:




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


Kode bekerja "di luar" dengan cara yang persis sama seperti pada opsi 2. Yaitu. objek, seolah-olah, menjadi "matryoshka" - di dalam satu objek, objek lain duduk dengan bodoh, dan ada "saluran komunikasi", yang menariknya, Anda dapat merujuk ke objek internal secara langsung.



Header spoiler
image



Di matlab, situasinya agak lebih menarik. Saat Anda menjalankan konstruktor turunan , MyClassB , tidak ada panggilan diam ke konstruktor leluhur MyClassA .



Anda perlu membuatnya secara langsung. Di satu sisi, ini mengganggu:




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


Tetapi jika turunan dipanggil dengan argumen lain, seperti MyClassB (d) , maka Anda dapat melakukan konversi di dalam, seperti:




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


Di C #, ini tidak dapat dilakukan secara langsung, dan ini menimbulkan kebutuhan untuk menulis semacam "fungsi konversi":




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


atau lakukan "konstruktor statis" seperti ini:




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


Sepertinya tentang warisan, pada dasarnya segalanya.



Tentu, tidak ada yang memaksa pewaris untuk menulis " F1 " metode dan " a " properti sehingga mereka selalu diterjemahkan ke dalam panggilan metode leluhur dan bidang. Penyiaran hanyalah perilaku "warisan" default. Bisa (tentunya! Ini metode lain di kelas lain ya bro) tulis seperti ini:








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


Enkapsulasi



... Secara konseptual, ini berarti bahwa di dalam objek kelas MyClassB, objek kelas MyClassA berada di bidang dasar, dengan kemampuan untuk menyiarkan perintah kontrol di luar. Semua ini sudah tertulis di atas dan tidak masuk akal untuk diulangi.



Ada topik seperti itu dengan pengubah akses yang berbeda - publik , pribadi , dilindungi ... Tentang mereka, apa yang paling menarik, ditulis di mana-mana secara lebih atau kurang normal, saya sarankan untuk membacanya saja.

publik - ini berarti bahwa bidang , properti ,atau metode akan terlihat dari luar dan dapat ditarik.

Jika Anda TIDAK tahu apa yang harus dilakukan, tulis di depan umum (nasihat buruk, ya).



Kemudian temukan kekuatan dalam diri Anda dan keluarkan publik ini (atau, untuk kejelasan, gantilah dengan pribadi ) di mana pun tidak perlu (lakukan "refactoring"). Ya, tentu saja, sangat baik menjadi seorang visioner, bertindak dalam pertempuran paranormal, dan langsung menebak di mana harus menjadikannya pribadi .

private - ini berarti bahwa bidang , properti ,atau metode objek "folder-file" hanya terlihat dari dalam metode kelas ini.

TAPI ... Ini adalah kelas, bukan INSTANCE (objek). Jika Anda memiliki kode seperti:




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


Hal seperti itu digunakan dalam kloning (lihat sumber lain untuk lebih jelasnya).



Saya mencoba memikirkan pengaturan publik dan privat ini saat menulis kode . Ini adalah kode yang memakan waktu yang tidak dapat diterima saat membuat kode secara kasar. Dan kemudian ternyata kode itu sendiri perlu dilakukan dengan cara yang berbeda secara fundamental.



Jika kode itu ditulis dalam solo, maka tidak masuk akal untuk repot-repot dengan privat dan publik sebelumnya, ada tugas yang lebih penting, misalnya, benar-benar datang dengan dan menulis kode ...

Satu-satunya tempat yang kurang lebih jelas di tempat apa meletakkan privat dan publik adalah sama properti terkenal yang merujuk ke beberapa jenis bidang.




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


Di tempat lain, untuk mengatur publik dan privat, Anda perlu benar-benar melihat apa yang dilakukan program, dan kemungkinan besar tidak akan berhasil untuk mempelajari "in absentia" ini.

protected - ini berarti " publik " untuk semua metode kelas turunan dan " privat " untuk yang lainnya.

Secara umum, logis jika kita mengasumsikan bahwa kelas yang diwariskan muncul hanya sebagai "versi yang lebih canggih" dari leluhur mereka.



Jujur saja, saya sudah lupa di mana saya secara eksplisit menerapkan perlindungan ini. Biasanya publik atau swasta. Sebagian besar kelas yang saya tulis tidak mewarisi dari kelas adat lainnya, dan di mana mereka mendapatkannya, jarang ada kebutuhan serius untuk hal-hal seperti itu.



Kesannya adalah bahwa pengubah non- publik diminati saat mengerjakan beberapa proyek besar, yang mungkin didukung oleh sekelompok orang ... Memahami di mana harus menerapkannya hanya muncul setelah sekian lama menempel ke kode sepanjang kilometer. Ketika mempelajari "melalui korespondensi", sulit untuk memberikan pemahaman ini.



Polimorfisme



Ketika saya menulis di Matlab, saya tidak mengerti mengapa polimorfisme dibutuhkan sama sekali dan APA ITU.

Kemudian, ketika saya beralih ke C #, saya menyadari bahwa ini adalah fitur BAHASA KHUSUS KETAT, dan ini memiliki hubungan yang sangat lemah dengan OOP. Di matlab, Anda bisa menulis di mana saja tanpa mengetahui adanya polimorfisme ini - tidak ada pengetikan yang tegas.



Untuk kesederhanaan, biarkan kelas disebut A dan B.




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


Ini disebut typecasting. Di C #, Anda dapat DIRI SENDIRI (jika Anda tahu caranya) menulis sistem pengecoran jenis buatan sendiri, dari hampir semua jenis ke jenis lainnya.

Di sini - hanya "melemparkan" ke luar kotak. Karena objek lain dari kelas A berada di dalam objek x milik kelas B , maka salah satu cara pengecoran yang tampak jelas adalah menutup semua koneksi dari objek eksternal ke internal. Ini sebenarnya tidak perlu, tetapi mereka yang menemukan "polimorfisme" memutuskan bahwa yang paling jelas adalah melakukannya. Dan pengguna akan menulis sendiri opsi lainnya. Maaf untuk "politota" (tidak lagi relevan) dari sampel 2008-2012.










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


Antarmuka



Kita harus mulai dengan cara menerapkan INI.



Katakanlah kita memiliki daftar dan kita ingin memasukkan sesuatu ke dalamnya.



Di matlab, cara termudah untuk melakukannya adalah (disebut array sel):




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


Anda tidak berpikir benda apa itu, Anda hanya mengambilnya dan memasukkannya ke dalam daftar.



Selanjutnya, katakanlah Anda perlu mengulang daftar dan melakukan sesuatu dengan setiap elemen:




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


Jika fungsi DoSomeStuff cukup pintar untuk mencerna apa pun yang dimasukkan ke dalamnya, kode ini AKAN TERPENUHI.



Jika fungsi DoSomeStuff (atau pembuatnya) tidak bersinar dengan kecerdasan, maka ada kemungkinan tersedak sesuatu: angka, garis, kelas buatan Anda sendiri, Iblis Botak, atau - Tuhan melarang - Nenek Anda.



MATLAB akan menunjukkan umpatan merah dalam bahasa Inggris di konsol dan menghentikan program Anda. Dengan demikian, kode Anda secara otomatis akan menerima Penghargaan Darwin.



Namun, ini sebenarnya buruk karena terkadang kodenya sangat kompleks. Kemudian Anda akan sangat yakin bahwa Anda melakukan semuanya dengan benar, tetapi pada kenyataannya, kombinasi tindakan yang salah tidak pernah diluncurkan selama pengujian.



Itulah sebabnya (meskipun bukan hanya karena) di MATLAB - Saya berhasil memastikannya sendiri (kira-kira seperti di KPDV), pada ukuran kode yang mengerikan - TIDAK PERLU menulis proyek besar.



Sekarang mari kita lanjutkan ke C #. Kami membuat daftar, dan ... dan kami diminta untuk segera menunjukkan JENIS objek. Kami membuat daftar tipe Daftar.



Dalam daftar seperti itu Anda dapat menempatkan nomor 1.



Dalam daftar seperti itu Anda dapat menempatkan nomor 2 dan bahkan, Tuhan ampuni saya, 3.




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


Tapi string teks sudah tidak ada lagi. Objek kelas buatan Anda - sama sekali tidak. Saya diam tentang Iblis Botak dan Nenek Anda, mereka tidak bisa berada di sana dalam varian apa pun.



Anda dapat membuat daftar baris terpisah. Anda bisa - untuk kelas buatan Anda sendiri.




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


Bahkan, Anda dapat membuat daftar - secara terpisah - Setan Botak, Nenek Anda.



Tetapi menambahkannya ke dalam satu daftar tidak akan berhasil. Kode Anda akan memenangkan Hadiah Darwin, dikombinasikan dengan penyalahgunaan kompiler bahkan sebelum Anda mencoba menjalankannya. Kompilator dengan hati-hati tidak mengizinkan Anda untuk membuat fungsi DoSomeStuff (item) , yang akan "tersedak" dengan argumennya.



Ini sangat berguna dalam proyek besar.

Tetapi apa yang harus dilakukan jika Anda masih ingin memasukkannya ke dalam satu daftar kecil?



Ini sebenarnya bukan masalah. Itu cukup untuk mengubah semuanya menjadi objek tipe . Hampir (atau bahkan mutlak) semuanya dapat diubah menjadi objek tipe .




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


Masalahnya dimulai saat kita mulai mengulang daftar. Intinya adalah bahwa tipe objek (hampir) tidak dapat melakukan apapun. Itu hanya bisa BE dari tipe objek .

- Apa yang bisa kau lakukan?

- Aku bisa menyanyi dan menari

- Dan aku - Sancho ...

- Apa yang bisa kamu lakukan, Sancho?

- Saya Sancho.

- Nah, bisakah kamu melakukan sesuatu?

- Anda tidak mengerti. Saya bisa menjadi Sancho.



Oleh karena itu, antarmuka ditulis. Ini adalah kelas yang ingin Anda warisi. Antarmuka berisi header metode dan properti.



Dalam kasus kami, ini adalah metode dan properti yang memastikan operasi NORMAL dari fungsi DoSomeStuff (item) . Antarmuka tidak menerapkan properti itu sendiri. Ini dilakukan dengan sengaja. Faktanya, seseorang bisa saja mewarisi dari beberapa kelas yang cocok untuk digunakan oleh fungsi DoSomeStuff () . Tapi itu berarti kode tambahan dan programmer yang pelupa.



Oleh karena itu, jika sesama programmer mewarisi sebuah antarmuka, tetapi lupa untuk mengimplementasikan properti dan metode yang diperlukan dari sebuah kelas, kompilator akan menuliskannya ke kode dengan Hadiah Darwin. Jadi, Anda bisa melakukan ini:




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


Itu. untuk itu, pada akhirnya, sebuah antarmuka diperlukan - untuk membuat daftar yang diketik, atau beberapa bidang di kelas, dan melewati larangan untuk menambahkan (ke daftar atau bidang) semacam sampah tersisa di sana.



Antarmukanya adalah "birokrasi". Tidak di semua tempat dan tidak di semua tempat yang dibutuhkan, meskipun ya, itu perlu dan berguna dalam proyek-proyek besar.



... secara umum, sesuatu seperti itu ... Saya minta maaf atas ekspresi kasarnya, untuk beberapa alasan menurut saya presentasi materi yang "kering" tidak akan berhasil ...



All Articles