Menulis kode, yang mudah diubah adalah pemrograman yang baik. Selamat Datang di surga pemrograman! Tapi beberapa hal jauh lebih sulit dalam kenyataannya: kode sumber sulit dimengerti, titik dependensi dalam arah yang tak terhitung jumlahnya, kopling menjengkelkan, dan Anda segera merasakan panasnya neraka pemrograman. Dalam tutorial ini, kita akan membahas beberapa prinsip, teknik dan ide-ide yang akan membantu Anda menulis kode yang mudah diubah.


Beberapa Konsep Berorientasi Objek

Pemrograman berorientasi objek (OOP) menjadi populer, karena menjanjikan organisasi dan penggunaan kembali kode program. Kami telah menggunakan konsep OOP selama bertahun-tahun sekarang, namun kami terus mengulangi untuk menerapkan logika yang sama dalam proyek-proyek kami. OOP memperkenalkan seperangkat prinsip dasar yang baik, jika digunakan dengan benar, dapat menyebabkan lebih baik, kode program yang bersih.

Kohesi

Hal-hal yang menjadi milik bersama harus dijaga bersama-sama, jika tidak, mereka harus dipindahkan ke tempat lain. Ini disebut kohesi. Contoh terbaik kohesi dapat ditunjukkan dengan kelas:

class ANOTCohesiveClass {
   private $firstNumber;
   private $secondNumber;
   private $length;
   private $width;
   function __construct($firstNumber, $secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function setLength($length) {
      $this->length = $length;
   }
   function setHeight($height) {
      $this->width = $height;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
   function area() {
      return $this->length * $this->width;
   }
}

Contoh ini mendefinisikan class dengan atribut yang mewakili angka dan ukuran. Sifat ini, hanya dinilai dengan nama mereka, bukan milik bersama. Kami kemudian memiliki dua metode, add() dan substract(), yang beroperasi hanya pada dua variabel nomor. Kami lebih lanjut memiliki suatu metode area(), yang beroperasi di atribut length dan width.

Hal ini jelas bahwa kelas ini bertanggung jawab untuk sekelompok informasi yang terpisah. Ini memiliki kohesi yang sangat rendah. Mari kita refactor.

class ACohesiveClass {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber, $secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}

Ini adalah kelas yang sangat kohesif. Kenapa? Karena setiap bagian dari kelas ini milik satu sama lain. Anda harus berusaha untuk kohesi, tapi hati-hati, bisa sulit untuk mencapai hal itu.

Ortogonalitas

Dalam istilah sederhana, ortogonalitas mengacu pada isolasi atau penghapusan efek samping. Sebuah metode, class atau modul yang mengubah keadaan class yang tidak terkait lainnya atau modul tidak ortogonal. Misalnya, kotak hitam pesawat terbang adalah orthogonal. Ia memiliki fungsionalitas sendiri, sumber kekuatan sendiri, mikrofon dan sensor. Hal ini tidak berpengaruh pada pesawat itu berada di dalam, atau di luar pesawat. Ia hanya menyediakan mekanisme untuk merekam dan mengambil data penerbangan.

Contoh dari satu sistem non-ortogonal tersebut adalah elektronik mobil Anda. Meningkatkan kecepatan kendaraan Anda memiliki beberapa efek samping, seperti meningkatkan volume suara radio (antara lain). Kecepatan tidak ortogonal terhadap mobil.

class Calculator {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber, $secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      $sum = $this->firstNumber + $this->secondNumber;
      if ($sum > 100) {
         (new AlertMechanism())->tooBigNumber($sum);
      }
      return $sum;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}
class AlertMechanism {
   function tooBigNumber($number) {
      echo $number . 'is too big!';
   }
}

Dalam contoh diatas, metode add() pada class Kalkulator  menunjukkan perilaku tak terduga: ia menciptakan objek anAlertMechanism dan memanggil salah satu metode tersebut. Ini adalah perilaku yang tak terduga dan tidak diinginkan, librari konsumen tidak akan pernah mengharapkan pesan dicetak ke layar. Sebaliknya, mereka hanya mengharapkan jumlah dari angka yang diberikan.

Contoh dibawah Ini lebih baik. AlertMechanism tidak berpengaruh pada Kalkulator. Sebaliknya, AlertMechanism menggunakan apa saja yang perlu dalam rangka untuk menentukan apakah peringatan harus dikeluarkan.

class Calculator {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber, $secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}
class AlertMechanism {
   function checkLimits($firstNumber, $secondNumber) {
      $sum = (new Calculator($firstNumber, $secondNumber))->add();
      if ($sum > 100) {
         $this->tooBigNumber($sum);
      }
   }
   function tooBigNumber($number) {
      echo $number . 'is too big!';
   }
}

Dependensi dan Coupling

Dalam kebanyakan kasus, dua kata ini sering dipertukarkan, tetapi, dalam beberapa kasus, satu istilah lebih disukai daripada yang lain.

Jadi apa apa itu dependesi/ketergantungan? Ketika objek A membutuhkan untuk menggunakan objek B, dalam rangka untuk melakukan perilaku yang ditentukan, kami mengatakan bahwa A tergantung pada B. Dalam OOP, dependensi sangat umum. Objek sering bekerja dengan dan bergantung pada satu sama lain. Jadi, sementara ini menghilangkan ketergantungan adalah hampir tidak mungkin untuk dilakukan. Mengontrol ketergantungan dan mengurangi mereka, bagaimanapun, adalah lebih baik.

Istilah, kopling berat dan kopling longgar, biasanya mengacu pada berapa banyak obyek tergantung pada objek lain.

Dalam sistem loosely-coupled, perubahan dalam satu objek memiliki efek lebih sedikit pada objek-objek lain yang bergantung padanya. Dalam sistem tersebut, class tergantung pada interface bukan implementasi konkret (kita akan berbicara lebih banyak tentang itu nanti). Inilah sebabnya mengapa sistem loosely-coupled lebih terbuka untuk modifikasi.

Coupling dalam atribut

Mari kita pertimbangkan contoh:

class Display {
   private $calculator;
   function __construct() {
      $this->calculator = new Calculator(1,2);
   }
}

Sudah umum melihat jenis kode seperti ini. Sebuah class, Display dalam kasus ini, tergantung pada class Kalkulator dengan langsung mereferensi class itu. Dalam kode di atas, atribut $calculator pada class Display adalah tipe Kalkulator. Objek yang mengisi atribut itu adalah hasil dari memanggil langsung konstruktor class Kalkulator ini.

Coupling dengan Mengakses Metode Class Lain

Tinjau kode berikut untuk mendemonstrasikan coupling semacam ini:

class Display {
   private $calculator;
   function __construct() {
      $this->calculator = new Calculator(1, 2);
   }
   function printSum() {
      echo $this->calculator->add();
   }
}

TClass Display memanggil method add() pada objek instansiasi class Calculator. Ini adalah bentuk kopling yang lain, karena satu kelas mengakses metode class lain.

Coupling dengan Method Reference

Kamu bisa mengkople class dengan method referensi, juga. Contoh:

class Display {
   private $calculator;
   function __construct() {
      $this->calculator = $this->makeCalculator();
   }
   function printSum() {
      echo $this->calculator->add();
   }
   function makeCalculator() {
      return new Calculator(1, 2);
   }
}

Sangat penting untuk dicatat bahwa method makeCalculator()  mengembalikan objek Kalkulator. Ini adalah ketergantungan.

Coupling dengan Polimorfisme

Warisan mungkin adalah bentuk terkuat dari ketergantungan:

class AdvancedCalculator extends Calculator {
   function sinus($value) {
      return sin($value);
   }
}

Tidak hanya AdvancedCalculator tidak dapat melakukan tugasnya tanpa Kalkulator, tetapi bahkan tidak bisa ada tanpa itu.

Mengurangi Coupling dengan Dependency Injection

One can reduce coupling by injecting a dependency. Here’s one such example:

Satu yang  dapat mengurangi kopling adalah dengan menyuntikkan ketergantungan. Berikut ini salah satu contohnya:

class Display {
   private $calculator;
   function __construct(Calculator $calculator = null) {
      $this->calculator = $calculator ? : $this->makeCalculator();
   }
// ... //
}

Dengan menyuntikkan objek Kalkulator melalui konstruktor Display, kita mengurangi ketergantungan Display pada kelas Kalkulator. Tapi ini hanya setengah dari solusi.

Mengurangi Kopling dengan Interface

Kita selanjutnya dapat mengurangi kopling dengan menggunakan interface. Sebagai contoh:

interface CanCompute {
   function add();
   function subtract();
}
class Calculator implements CanCompute {
   private $firstNumber;
   private $secondNumber;
   function __construct($firstNumber, $secondNumber) {
      $this->firstNumber = $firstNumber;
      $this->secondNumber = $secondNumber;
   }
   function add() {
      return $this->firstNumber + $this->secondNumber;
   }
   function subtract() {
      return $this->firstNumber - $this->secondNumber;
   }
}
class Display {
   private $calculator;
   function __construct(CanCompute $calculator = null) {
      $this->calculator = $calculator ? : $this->makeCalculator();
   }
   function printSum() {
      echo $this->calculator->add();
   }
   function makeCalculator() {
      return new Calculator(1, 2);
   }
}

Anda dapat menganggap ISP sebagai prinsip kohesi-tingkat yang lebih tinggi.

Kode ini memperkenalkan interface CanCompute. Sebuah interface adalah sebagai abstrak seperti yang Anda dapatkan dalam OOP, ia mendefinisikan anggota yang kelas harus implementasikan. Dalam kasus contoh di atas, Kalkulator mengimplementasikan interface CanCompute.

Konstruktor Display mengharapkan sebuah objek yang mengimplementasikan interface CanCompute. Pada titik ini, ketergantungan Display dengan Calculator secara efektif rusak. Setiap saat, kita bisa membuat kelas lain yang mengimplementasikan CanCompute dan melewatkan sebuah objek dari kelas itu pada konstruktor Display. Display sekarang hanya bergantung pada interface CanCompute, tapi bahkan ketergantungan yang opsional. Jika kita tidak melewatkan argumen apapun pada konstruktor Display, itu hanya akan membuat objek klasik Calculator  dengan memanggil makeCalculator (). Teknik ini sering digunakan, dan sangat membantu untuk pengembangan berbasis testing (TDD).


Prinsip-prinsip SOLID

SOLID adalah seperangkat prinsip untuk menulis kode yang bersih, yang kemudian membuatnya lebih mudah untuk berubah, mempertahankan dan memperluasnya di masa depan. Mereka adalah rekomendasi yang, bila diterapkan pada kode sumber, memiliki efek positif pada pemeliharaan.

Sejarah Singkat

Prinsip-prinsip SOLID, juga dikenal sebagai prinsip Agile, awalnya didefinisikan oleh Robert C. Martin. Meskipun ia tidak menemukan semua prinsip ini, dia adalah orang yang menempatkan mereka bersama-sama. Anda dapat membaca lebih banyak tentang mereka dalam bukunya: Agile Software Development, Principles, Patterns, and Practices. Prinsip SOLID mencakup beragam topik, tapi saya akan menyajikan mereka sesederhana yang aku mampu. Jangan ragu untuk meminta rincian tambahan di komentar, jika diperlukan.

Single Responsibility Principle (SRP)

Sebuah Kelas memiliki tanggung jawab tunggal. Hal ini mungkin terdengar sederhana, tetapi kadang-kadang bisa sulit untuk memahami dan mempraktekkan.

class Reporter {
   function generateIncomeReports();
   function generatePaymentsReports();
   function computeBalance();
   function printReport();
}

Siapa yang menurut Anda mengambil manfaat dari perilaku class ini? Nah, suatu departemen akuntansi merupakan pilihan (untuk saldo), departemen keuangan mungkin lainnya (untuk laporan pendapatan/pembayaran ), dan bahkan departemen pengarsipan bisa mencetak dan mengarsip laporan.

Ada empat alasan mengapa Anda mungkin harus mengubah kelas ini, setiap departemen mungkin ingin metode masing-masing disesuaikan untuk kebutuhan mereka.

SRP merekomendasikan memecah class tersebut menjadi lebih kecil, class berkelakuan khusus, masing-masing memiliki hanya satu alasan untuk berubah. Kelas tersebut cenderung sangat kohesif dan loosely coupled. Dalam arti, SRP adalah kohesi yang didefinisikan dari sudut pandang pengguna.

Open-Closed Principle (OCP)

Kelas (dan modul) harus menyambut perpanjangan fungsi mereka, serta menolak modifikasi fungsi mereka saat ini. Mari kita bermain dengan contoh klasik dari sebuah kipas angin listrik. Anda memiliki tombol dan Anda ingin mengontrol kipas angin. Jadi, Anda bisa menulis suatu kode seperti ini:

class Switch_ {
   private $fan;
   function __construct() {
      $this->fan = new Fan();
   }
   function turnOn() {
      $this->fan->on();
   }
   function turnOff() {
      $this->fan->off();
   }
}

Warisan mungkin adalah bentuk terkuat dari ketergantungan.

Kode ini mendefinisikan kelas Switch_ yang menciptakan dan mengendalikan obyek Fan. Harap perhatikan garis bawah setelah “Switch_”. PHP tidak memungkinkan Anda untuk menentukan kelas dengan nama “Switch.”

Bos kamu memutuskan bahwa ia ingin mengontrol cahaya dengan tombol yang sama. Ini adalah masalah, karena Anda harus mengubah Switch_.
Modifikasi pada kode yang ada adalah berisiko, bagian lain dari sistem mungkin akan terpengaruh dan memerlukan modifikasi lebih jauh. Itu selalu lebih baik untuk meninggalkan fungsi yang ada saja, saat menambahkan functionlaity baru.

Dalam terminologi OOP, Anda dapat melihat bahwa Switch_ memiliki ketergantungan kuat pada Fan. Di sinilah masalah kita terletak, dan di mana kita harus membuat perubahan kami.

interface Switchable {
   function on();
   function off();
}
class Fan implements Switchable {
   public function on() {
      // code to start the fan
   }
   public function off() {
      // code to stop the fan
   }
}
class Switch_ {
   private $switchable;
   function __construct(Switchable $switchable) {
      $this->switchable = $switchable;
   }
   function turnOn() {
      $this->switchable->on();
   }
   function turnOff() {
      $this->switchable->off();
   }
}

Solusi ini memperkenalkan antarmuka Switchable. Ia mendefinisikan metode bahwa semua benda switch-enabled perlu untuk mengimplementasikannya. Fan menerapkan Switchable, dan Switch_ menerima referensi ke sebuah objek Switchable dalam konstruktor.

Bagaimana ini bisa membantu kita

Pertama, solusi ini memecahkan ketergantungan antara Switch_ dan Fan. Switch_ tidak tahu bahwa itu dimulai dengan kipas/fan, juga tidak peduli. Kedua, memperkenalkan kelas Light tidak akan mempengaruhi Switch_ atau Switchable. Apakah Anda ingin mengontrol objek Light dengan kelas Switch_ Anda? Cukup membuat objek Light dan melewatkannya ke Switch_, seperti ini:

class Light implements Switchable {
   public function on() {
      // code to turn ligh on
   }
   public function off() {
      // code to turn light off
   }
}
class SomeWhereInYourCode {
   function controlLight() {
      $light = new Light();
      $switch = new Switch_($light);
      $switch->turnOn();
      $switch->turnOff();
   }
}

Liskov Substitution Principle (LSP)

LSP menyatakan bahwa class anak tidak boleh melanggar fungsi dari kelas induk. Hal ini sangat penting karena konsumen dari class induk mengharapkan class untuk berperilaku dengan cara tertentu. Melewatkan class anak ke konsumen hanya harus bekerja dan tidak mempengaruhi fungsi aslinya.

Ini membingungkan pada pandangan pertama, jadi mari kita lihat contoh klasik lain:

class Rectangle {
   private $width;
   private $height;
   function setWidth($width) {
      $this->width = $width;
   }
   function setHeigth($heigth) {
      $this->height = $heigth;
   }
   function area() {
      return $this->width * $this->height;
   }
}

Contoh ini mendefinisikan kelas Rectangle sederhana. Kita dapat mengatur tinggi dan lebar, dan metode area () yang menyediakan luas persegi panjang. Menggunakan kelas Rectangle bisa terlihat seperti berikut:

class Geometry {
   function rectArea(Rectangle $rectangle) {
      $rectangle->setWidth(10);
      $rectangle->setHeigth(5);
      return $rectangle->area();
   }
}

metode rectArea()  menerima objek Rectangle sebagai argumen, menetapkan tinggi dan lebar, dan mengembalikan luasnya.

Di sekolah, kita diajarkan bahwa persegi adalah juga persegi panjang. Hal ini mengisyaratkan bahwa jika kita memodelkan program kami ke objek geometris, kelas Square harus meng-extends kelas Rectangle. Class tersebut akan terlihat seperti apa?

class Square extends Rectangle {
   // What code to write here?
}

Saya memiliki waktu yang sulit mencari tahu apa yang harus ditulis di kelas Square. Kami memiliki beberapa pilihan. Kita bisa mengganti metode area() dan mengembalikan kuadrat  dari $width:

class Rectangle {
   protected $width;
   protected $height;
   // ... //
}
class Square extends Rectangle {
   function area() {
      return $this->width ^ 2;
   }
}

Perhatikan bahwa saya mengubah atribut Rectangle menjadi protected, memberikan akses Square ke atribut tersebut. Hal ini tampak wajar dari sudut pandang geometri. Sebuah persegi memiliki sisi yang sama; mengembalikan kuadrat dari lebar adalah wajar.

Namun, kami memiliki masalah dari sudut pandang pemrograman. Jika Square adalah Rectangle, kita seharusnya tidak memiliki masalah memasukkannya ke dalam kelas Geometri. Tapi, dengan demikian, Anda dapat melihat bahwa kode Geometri ini tidak masuk akal, ia menetapkan dua nilai yang berbeda untuk tinggi dan lebar. Inilah sebabnya mengapa persegi bukan persegi panjang dalam pemrograman. LSP dilanggar.

Interface Segregation Principle (ISP)

Unit test harus berlari cepat – sangat cepat.

Prinsip ini berkonsentrasi pada memecah interface besar menjadi kecil, interface khusus. Ide dasarnya adalah bahwa konsumen yang berbeda dari kelas yang sama tidak harus tahu tentang interface yang berbeda – hanya interface perlu digunakan konsumen. Bahkan jika konsumen tidak secara langsung menggunakan semua metode umum pada objek, masih tergantung pada semua metode. Jadi mengapa tidak menyediakan interface yang hanya menyatakan metode yang perlu digunakan setiap pengguna?

Hal ini mendekati bahwa interface harus dimiliki oleh klien dan tidak implementasinya. Jika Anda menyesuaikan interface Anda ke kelas yang mengkonsumsi, mereka akan menghormati ISP. Pelaksanaan itu sendiri dapat menjadi unik, karena kelas dapat mengimplementasikan beberapa interface.

Mari kita bayangkan bahwa kita mengimplementasikan aplikasi pasar saham. Kami memiliki broker yang membeli dan menjual saham, dan dapat melaporkan pendapatan dan kerugian hariannya. Implementasi yang sangat sederhana akan mencakup sesuatu seperti antarmuka Broker, kelas NYSEBroker yang mengimplementasikan Broker dan beberapa kelas antarmuka pengguna: satu untuk menciptakan transaksi (TransactionsUI) dan satu untuk pelaporan (DailyReporter). Kode untuk sistem seperti itu bisa mirip dengan berikut ini:

interface Broker {
   function buy($symbol, $volume);
   function sell($symbol, $volume);
   function dailyLoss($date);
   function dailyEarnings($date);
}
class NYSEBroker implements Broker {
   public function buy($symbol, $volume) {
      // implementsation goes here
   }
   public function currentBalance() {
      // implementsation goes here
   }
   public function dailyEarnings($date) {
      // implementsation goes here
   }
   public function dailyLoss($date) {
      // implementsation goes here
   }
   public function sell($symbol, $volume) {
      // implementsation goes here
   }
}
class TransactionsUI {
   private $broker;
   function __construct(Broker $broker) {
      $this->broker = $broker;
   }
   function buyStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->buy($data['sybmol'], $data['volume']);
   }
   function sellStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->sell($data['sybmol'], $data['volume']);
   }
}
class DailyReporter {
   private $broker;
   function __construct(Broker $broker) {
      $this->broker = $broker;
   }
   function currentBalance() {
      echo 'Current balace for today ' . date(time()) . "\n";
      echo 'Earnings: ' . $this->broker->dailyEarnings(time()) . "\n";
      echo 'Losses: ' . $this->broker->dailyLoss(time()) . "\n";
   }
}

Sementara kode ini dapat bekerja, hal itu melanggar ISP. Kedua DailyReporter dan TransactionUI tergantung pada antarmuka Broker. Namun, mereka masing-masing hanya menggunakan sebagian kecil dari antarmuka. TransactionUI menggunakan metode buy() dan sell(), sedangkan DailyReporter menggunakan metode dailyEarnings() dan dailyLoss() .

Anda mungkin berpendapat bahwa Broker tidak kohesif karena memiliki metode yang tidak terkait, dan dengan demikian tidak milik bersama.

Ini mungkin benar, tapi jawabannya tergantung pada implementasi Broker, menjual dan membeli mungkin sangat terkait dengan kerugian saat ini dan laba. Misalnya, Anda mungkin tidak diperbolehkan untuk membeli saham jika Anda kehilangan uang.

Anda juga mungkin berpendapat bahwa Broker juga melanggar SRP. Karena kita memiliki dua kelas yang menggunakannya dengan cara yang berbeda, mungkin ada dua pengguna yang berbeda. Yah, aku bilang tidak. Satu-satunya pengguna mungkin adalah broker yang sebenarnya. Dia ingin membeli, menjual, dan melihat dana mereka saat ini. Tapi sekali lagi, jawaban yang sebenarnya tergantung pada seluruh sistem dan bisnis.

ISP pasti dilanggar. Kedua kelas UI tergantung pada seluruh Broker. Ini adalah masalah umum, jika Anda berpikir interface milik implementasinya. Namun, pergeseran sudut pandang bisa menyarankan desain berikut:

interface BrokerTransactions {
   function buy($symbol, $volume);
   function sell($symbol, $volume);
}
interface BrokerStatistics {
   function dailyLoss($date);
   function dailyEarnings($date);
}
class NYSEBroker implements BrokerTransactions, BrokerStatistics {
   public function buy($symbol, $volume) {
      // implementsation goes here
   }
   public function currentBalance() {
      // implementsation goes here
   }
   public function dailyEarnings($date) {
      // implementsation goes here
   }
   public function dailyLoss($date) {
      // implementsation goes here
   }
   public function sell($symbol, $volume) {
      // implementsation goes here
   }
}
class TransactionsUI {
   private $broker;
   function __construct(BrokerTransactions $broker) {
      $this->broker = $broker;
   }
   function buyStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->buy($data['sybmol'], $data['volume']);
   }
   function sellStocks() {
      // UI logic here to obtain information from a form into $data
      $this->broker->sell($data['sybmol'], $data['volume']);
   }
}
class DailyReporter {
   private $broker;
   function __construct(BrokerStatistics $broker) {
      $this->broker = $broker;
   }
   function currentBalance() {
      echo 'Current balace for today ' . date(time()) . "\n";
      echo 'Earnings: ' . $this->broker->dailyEarnings(time()) . "\n";
      echo 'Losses: ' . $this->broker->dailyLoss(time()) . "\n";
   }
}

Hal ini sebenarnya masuk akal dan menghormati ISP. DailyReporter hanya bergantung pada BrokerStatistics, ia tidak peduli dan tidak harus tahu tentang operasi penjualan dan pembelian. TransactionsUI, di sisi lain, hanya mengenal tentang membeli dan menjual. NYSEBroker identik dengan kelas kami sebelumnya, kecuali sekarang menerapkan interface BrokerTransactions dan BrokerStatistics.

Anda dapat menganggap ISP sebagai prinsip kohesi pada tingkat yang lebih tinggi.

Ketika kedua kelas UI tergantung pada interface Broker, mereka mirip dengan dua kelas, masing-masing memiliki empat atribut, yang kedua atribut digunakan dalam satu metode dan dua lainnya dalam metode lain. Kelas akan belum sangat kohesif.

Sebuah contoh yang lebih kompleks prinsip ini dapat ditemukan di salah satu paper pertama Robert C. Martin pada subjek: The Interface Segregation Principle.

Dependency Inversion Principle (DIP)

Prinsip ini menyatakan bahwa modul-tingkat tinggi tidak harus tergantung pada modul tingkat rendah, keduanya harus bergantung pada abstraksi. Abstraksi tidak harus tergantung pada detail, rincian harus bergantung pada abstraksi. Sederhananya, Anda harus bergantung pada abstraksi sebanyak mungkin dan tidak pernah pada implementasi nyata.

Caranya dengan DIP adalah bahwa Anda ingin untuk membalikkan ketergantungan, tapi selalu ingin menjaga aliran kontrol. Mari kita tinjau contoh kita dari OCP (kelas Switch dan Light). Dalam pelaksanaannya, kami memiliki saklar langsung mengendalikan lampu.

As you can see, both dependency and control flow from Switch toward Light. While this is what we want, we do not want to directly depend on Light. So we introduced an interface.

Seperti yang Anda lihat, ketergantungan dan aliran kontrol, dari Switchmenuju Light. Sementara ini adalah apa yang kita inginkan, kita tidak ingin langsung bergantung pada cahaya. Jadi kami memperkenalkan sebuah antarmuka.

Sungguh menakjubkan betapa hanya memperkenalkan antarmuka membuat kode kita menghormati kedua DIP dan OCP. Seperti yang Anda lihat, tidak ada class tergantung pada pelaksanaan nyata dari class Light, dan keduanya Light dan Switch tergantung pada antarmuka theSwitchable. Kami membalik ketergantungan, dan aliran kontrol tidak berubah.


Desain Tingkat Tinggi

Another important aspect of your code is your high-level design and general architecture. An entangled architecture produces code that is hard to modify. Keeping a clean architecture is essential, and the first step is understanding how to separate your code’s different concerns.

Aspek penting lain dari kode Anda adalah desain tingkat tinggi dan arsitektur umum. Sebuah arsitektur terjerat menghasilkan kode yang sulit untuk memodifikasi. Menjaga arsitektur bersih sangat penting, dan langkah pertama adalah memahami bagaimana memisahkan kode Anda pada persoalan yang berbeda.

Dalam gambar ini, saya mencoba untuk meringkas perhatian utama. Di tengah-tengah skema logika bisnis kami. Ini harus terisolasi-baik dari seluruh dunia, dan mampu bekerja dan berperilaku seperti yang diharapkan tanpa adanya salah satu bagian lain. Melihatnya sebagai ortogonal pada tingkat yang lebih tinggi.

Mulai dari kanan, Anda memiliki “Main” Anda – titik masuk ke aplikasi – dan pabrik-pabrik yang membuat objek. Sebuah solusi yang ideal akan mendapatkan objek tersebut dari pabrik khusus, tapi itu sebagian besar tidak mungkin atau tidak praktis. Namun, Anda harus menggunakan pabrik ketika Anda memiliki kesempatan untuk melakukannya, dan menjaga mereka di luar logika bisnis Anda.

Kemudian, di bagian bawah (oranye), kita memiliki persistence (database, pengaksesan berkas, jaringan komunikasi) untuk tujuan menyimpan informasi. Tidak ada objek dalam logika bisnis kami harus tahu bagaimana persistence bekerja.

Di sebelah kiri adalah mekanisme pengiriman.

MVC, seperti Laravel atau CakePHP, seharusnya hanya mekanisme pengiriman, tidak lebih.

Hal ini memungkinkan Anda memindahkan satu mekanisme dengan yang lain tanpa menyentuh logika bisnis Anda. Ini mungkin terdengar keterlaluan untuk sebagian dari Anda. Kami diberitahu bahwa logika bisnis kami harus ditempatkan dalam model kami. Yah, saya tidak setuju. Model kami harus “request model”, yaitu objek data bodoh yang digunakan untuk menyampaikan informasi dari MVC dengan logika bisnis. Opsional, saya tidak melihat masalah memasukkan validasi input dalam model, tapi tidak lebih. Logika bisnis tidak harus berada dalam model.

Ketika Anda melihat arsitektur aplikasi Anda atau struktur direktori, Anda akan melihat sebuah struktur yang menunjukkan apa yang  program kerjakan bertentangan dengan apa framework atau database yang Anda gunakan.

 

Terakhir, pastikan bahwa semua titik dependensi terhadap logika bisnis kami. User interface, factories, database adalah implementasi sangat konkret, dan Anda tidak harus bergantung pada mereka. Pembalikan dependensi mengarah ke arah logika bisnis kami, memodularkan sistem kami, memungkinkan kita untuk mengubah dependensi tanpa memodifikasi logika bisnis.


Beberapa Pemikiran tentang Design Patterns

Design patterns play an important role in making code easier to modify, by offering a common design solution that every programmer can understand. From a structural point-of-view, design patterns are obviously advantageous. They are well tested and thought-out solutions.

Desain patterns memainkan peran penting dalam membuat kode lebih mudah untuk dimodifikasi, dengan menawarkan solusi desain umum bahwa setiap programmer bisa mengerti. Dari point-of-view struktural, pola desain yang jelas menguntungkan. Mereka diuji dengan baik dan memberikan solusi.


Kekuatan Pengujian

Test-Driven Development encourages writing code that is easy to test. TDD forces you to respect most of the above principles in order to make your code easy to test. Injecting dependencies and writing orthogonal classes are essential; otherwise, you end up with huge test methods. Unit tests should run fast – very fast, actually, and everything that is not tested should be mocked. Mocking many complex classes for a simple test can be overwhelming. So when you find yourself mocking ten objects to test a single method on a class, you may have a problem with your code… not your test.

Pengembangan Berbasis Testing mendorong menulis kode yang mudah untuk diuji. TDD memaksa Anda untuk menghormati sebagian besar prinsip-prinsip di atas dalam rangka untuk membuat kode Anda mudah untuk diuji. Penyuntikan ketergantungan dan menulis kelas yang ortogonal sangat penting, jika tidak, Anda berakhir dengan pengujian metode yang besar. Unit test harus berlari cepat – sangat cepat.

Kesimpulan

Pada akhirnya, itu semua bermuara pada berapa banyak Anda peduli tentang kode sumber Anda. Memiliki pengetahuan teknis tidak cukup, Anda perlu menerapkan pengetahuan itu lagi dan lagi, tidak pernah menjadi 100% puas dengan kode Anda. Anda harus ingin membuat kode Anda mudah untuk dipertahankan, dibersihkan dan terbuka untuk berubah.

Diterjemahkan secara bebas dari http://net.tutsplus.com/tutorials/how-to-write-code-that-embraces-change/

Advertisements

About phpgeek programmer

pemimpi yang berharap menjadi the best programmer di zamannya

One response »

  1. nofit says:

    thanks gan atas artikelnya.. cukup membantu buat ane 😀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s