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









2 comments:

MK said...

Mengenai SRP dalam konteks function, boleh digunakan sebagai guideline, tapi takleh la ikut je. (refer o Clean Code, Chapter 3:Functions, p38).

Ada sesetengah function tak boleh nak dipecahkan lagi tanpa menambah complexity. Tapi kalau violate SRP byk sangat, mesti ada sesuatu yang tak kena.

Listing 3-4
Payroll.java
public Money calculatePay(Employee e)
throws InvalidEmployeeType {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
There are several problems with this function. First, it’s large, and when new
employee types are added, it will grow. Second, it very clearly does more than one thing.
Third, it violates the Single Responsibility Principle7 (SRP) because there is more than one
reason for it to change. Fourth, it violates the Open Closed Principle8 (OCP) because it
must change whenever new types are added. But possibly the worst problem with this
function is that there are an unlimited number of other functions that will have the same
structure. For example we could have
isPayday(Employee e, Date date),
or
deliverPay(Employee e, Money pay),
or a host of others. All of which would have the same deleterious structure.
The solution to this problem (see Listing 3-5) is to bury the switch statement in the
basement of an ABSTRACT FACTORY,9 and never let anyone see it. The factory will use the
switch statement to create appropriate instances of the derivatives of Employee, and the var-
ious functions, such as calculatePay, isPayday, and deliverPay, will be dispatched poly-
morphically through the Employee interface.


Dalam konteks class pulak, blom sampai situ lagi. Hehe.

ryzam said...

Thanks, menarik.. ya aku setuju SRP sebagai guideline bagai memperkemaskan code. Sebab itu kalau lihat posting aku sebelum ini SRP aku gunakan guideline apabila nak buat refactoring, sebab semasa first design iteration benda nie kenkadang masih lagi tak clear dan samar.

Maka apabila dah masuk ke 2nd iteration dan seterusnya dan code semakin bertambah keperluan untuk refactoring semakin tinggi.

Ok dari Payroll.java nie SRP violates apabila calculateCommissionePay, calculateSalariedPay dan calculateHourlyPay semuanya berada dalam class Payroll, itu yang menjadikan Payroll buat kerja-kerja yang bukan sepatutnya dibuat.