Showing posts with label SRP. Show all posts
Showing posts with label SRP. Show all posts

Wednesday, April 22, 2009

Transaformasi kepada Single Responsibility Principal

Post aku sebelum nie ada menyentuh tentang kepentingan pattern SRP dan OCP dalam mencorakkan design domain dan sewaktu coding. Tapi bila aku bercerita tentang kepentingan pattern jika tidak diterjemahkan kedalam code dan real implemantation agak payah hendak mendapat gambaran yang jelas dan kebaikan pattern tersebut. Aku ada berbincang dengan kawan seangkatan berkaitan kedua-dua pattern dan apa yang pasti SRP merupakan pattern yang agak tricky. Jadi aku cuba terangkan dengan layman word dan code.

Aku tiada banyak idea nak bagi contoh dan aku pun tak pasti contoh yang aku bawa ini bagus atau tidak, ini juga menjadi kayu pengukur untuk aku mengetahui kefahaman aku dalam pattern2 ini.

Apa dia SRP?..

Single Responsibility Principle

Mengikut tuan empunya diri yang memperkenalkan pattern ini, SRP ialah
Sesuatu class/object hanya ada satu sebab sahaja untuk diubah..

Aku bawah contoh code dibawah :


    public class Report
{
public void Print()
{
//
}

public IList GetData()
{
return null;
}

public void FormatReport()
{
//
}
}


Kenapa code ini melanggar principle SRP. Kalau dilihat code tersebut ada berapa kerja code Report itu boleh buat? Ada 3, iaitu dia boleh melakukan tugas Print(), tugas untuk Formatting FormatReport() dan tugas untuk mendapatkan data GetData(). Oleh kerana itu code tersebut ada 3 sebab utama jika kita hendak buat perubahan.

Jadi adakah setiap class/object hanya boleh melakukan satu tugas sahaja bagi memenuhi SRP? Jawapannya sudah tentulah tidak. Kena faham maksud responsibility (tanggungjawab). Ok seorang bapa mempunyai banyak tanggungjawab, salah satu ialah menyara anak-anak. Jika beliau mempunyai 3 orang anak adakah perlu diwujudkan 3 class berkaitan saraan? Jawapan ialah tidak. Object saraan hanyalah satu tapi tugasnya boleh ada banyak selagi mana ianya berada dalam lingkungan makna saraan.

Caranya ialah dengan groupkan mana-mana tugas yang related dengan responsibility class tersebut dan refactorkan.

Contoh:

public class Bapa
{
public void Bekerja()
{
}

public void SaraanAnak1()
{
}

public void SaraanAnak2()
{
}

public void SaraanAnak3()
{
}

public void SaraanIbuBapa()
{
}

}


Jadi berdasarkan code diatas ini class bapa coupling direct dengan tugas-tugas tersebut, aku boleh bahagikan kepada 3 responsiblity disini seperti berikut.
1. Bekerja
2. Saraan Anak
3. Saraan IbuBapa (Bapa/Ibu kepada bapa tersebut)

Jadi bila refactor dia akan menjadi:

public class Bapa
{

public void Tugas()
{
new SaraanIbuBapa();
new SaraanAnak();
new Kerja();
}
}

public class Kerja
{
public Kerja()
{
Bekerja();
}

public void Bekerja()
{
}
}

public class SaraanAnak
{
public SaraanAnak()
{
SaraanAnak1();
SaraanAnak2();
SaraanAnak3();
}

public void SaraanAnak1()
{
}

public void SaraanAnak2()
{
}

public void SaraanAnak3()
{
}
}

public class SaraanIbuBapa
{
public SaraanIbuBapa()
{
SaraanIbuBapaSendiri();
SaraanIbuBapaMertua();
}

public void SaraanIbuBapaSendiri()
{
}

public void SaraanIbuBapaMertua()
{
}
}


So dari class Bapa yang sarat dgn tugas kita dah menjadikan class Bapa untuk follow SRP. Ini adalah secara basic, class SaraanAnak sendiri adalah grouping tugas secara highlevel kemungkinan juga akan ada refactor di class tersebut tetap ada. Aku cuba buat sesimple yang boleh

Cuba-cubalah memahami, ada yang nak bagi comment dipersilakan.

Friday, April 03, 2009

Single Responsibility Principle

Aku terbaca satu artikel Would Your Objects Praise You?
cuma aku kurang setuju dengan apa yang mamat nie cakap. Ada benarnya tapi tak semuanya betul konsep tu. Apa yang cuba disampaikan ialah konsep Single Responsibility Principal.

The single responsibility principle states that every object should have a single responsibility, and that all its services should be narrowly aligned with that responsibility. [Wikipedia].

Ok, cara dan formula yang di gunakan cukup menarik dan bagus dan aku pun rasa sesuatu yang reasonable unutk digunakan apabila buat domain model anlysis. Formula yang cuba diketengahkan ialah

Can a [type] [action in the infinitive] itself?

So dalam artikel tersebut , mamat nie ambil contoh sebuah kereta


Jadi berdasarkan method yang ada pada kereta tersebut kalau dilihat memang menyalahi SRP. Sebab class Car coupling dengan telalu banyak kerja yang perlu dilakukan. Cuba fikirkan sejenak apa tugas sebuah kereta. Jawapan dia abstract dan berlainan bagi setiap orang, sebab itu SRP nie kalau mengikut pencetus principle yang dekenali sebagai Pakcik Bob pattern yang simple tapi susah untuk dapat yang betul betul ngam.

"The SRP is one of the simplest of the principle, and one of the hardest to get right. Conjoining responsibilities is something that we do naturally. Finding and separating those responsibilities from one another is much of what software design is really about".

Berbalik semula kepada persoalan kereta tadi. Mamat Brian nie cadangkan untuk dapatkan cara yang betul penggunaan SRP ialah dengan melakukan pertanyaan seperti berikut:

  • Can a car change gears by itself?
  • Can a car change a radio station by itself?
  • Can a car drive itself?
  • Can a car idle by itself?
  • Can a car park itself?
  • Can a car start itself?
Dan ini jawapan bagi soalan-soalan tersebut

Unless you’re the lucky owner of the Knight Rider, you probably answered ‘NO’ to a few of these questions. For every question or observation you have answered ‘NO’, you should more than likely refactor the class and move some of its responsibilities elsewhere. But where exactly can you move these responsibilities? Well, before we dive to some of these answers, let’s see if we can directly some of the original questions pertaining to our Car class:
  • Can a car change gears by itself? ANSWER: Sure, if it has an automatic transmission. Otherwise, only the driver can shift gears.
  • Can a car change a radio station by itself? ANSWER: Nope, only the driver (or the passenger) can change the radio station.
  • Can a car drive itself? ANSWER: Not unless it’s KITT! Only a driver can drive a car.
  • Can a car idle by itself? ANSWER: Sure it can.
  • Can a car park itself? ANSWER: Nope! Only a driver can know how to park a car.
  • Can a car start itself? ANSWER: Uh, no. Once again, only the driver can start the car.
Nie pula jawapan aku. Ya memang betul kereta tak boleh change gear sendiri tapi kereta simpan maklumat tentang gear, jadi jika kereta tak boleh nak buat tugas change gear tapi kereta ada maklumat tentang gear jadi nak buat macam mana nie?.. Kalau tanya BIJAN dia jawab kita perlukan kereta yang ada ciri GLOKAL .. hahahah (iklan sebentar). So mamat Brian nie kata keluarkan tugas tersebut kepada seorang Driver (role). Aku tak setuju sangat. Nie contoh yang dia beri dan apa yang diberi sebenarnya valid dan boleh digunakan cuma jika diteliti dengan lebih detail pasti jawapan lain sedikit.


Disini kena faham konsep message to object. Persoalan Driver perlu ada atau tidak masih lagi abstract kepada Domain Expert dan juga untuk apa application ini dihasilkan. Kalau application nie dihasilkan untuk stimulate Video Game dimana dari screen hanya nampak kereta yang boleh dipandu maka class Driver tidak diperlukan sebaliknya class Driver itu digantikan dengan Application Service - CarService.

Ini pula pada pendapat aku, yang dikategorikan sebagai programmer kampung. Untuk mengikut konsep SRP kita perlu pecahkan tugas-tugas tersebut kepada yang berhak, jangan tamak nak ambil tugas orang lain, nasib baik BIJAN tak tamak dia ambik sikit je. Dari class Car kita akan pecahkan kepada class Gear, class Radio, class Engine.


//Car.cs

public class Car

{

public Gear Gear { set; get; }

public Engine Engine { set; get; }

public Radio Radio { set; get; }

public void Drive()

{

}

}


//Engine.cs

public class Engine

{

public void Start()

{

}

}

//Gear.cs

public class Gear

{

public void ChangeGear()

{

}

}

//Radio.cs

public class Radio

{

public void ChangeRadioStation()

{

}

}


//CarService.cs


public class CarService
{
public Car Car { set; get; }

public void ChangeGear()
{
Car.Gear.ChangeGear();
}

public void ChangeRadioStation()
{
Car.Radio.ChangeRadioStation();
}

public void StartEngine()
{
Car.Engine.Start();
}

public void Drive()
{
Car.Drive();
}
}