Pages

Selasa, 10 Juli 2012

Bridge Pattern

Bridge Pattern ini merupakan pattern yang kuat (powerfull) dan sering
digunakan dalam pengembangan. Dan ini sebanding dengan usaha untuk mempelajarinya yang cukup menantang.
Untuk memahami design pattern yang satu ini, kita perlu melihat makna decouple (tidak berpasangan), abstraction (abstraksi), dan implementation (implementasi) dari sisi yang berbeda terlebih dahulu.
Decouple adalah bagaimana objek tidak terhubung satu sama lain, berdiri sendiri (independent), terpisah.
Abstraksi adalah konsep bagaimana antar objek saling terhubung.
Sedangkan “implementasi” janganlah dipandang sebagai penurunan dari suatu kelas abstrak (concrete class). Pandanglah “implementasi” sebagai objek lain yang digunakan oleh kelas abstrak (dan turunannya) untuk mengimplementasi dirinya (“nya” ditujukan untuk kelas abstrak).
Dalam sebagian besar tutorial, bridge pattern didefinisikan sebagai

pattern yang berusaha memisahkan antara abstraksi dan implementasi.
Bingung? Hehe.. Namanya juga teori, biasanya mengawang-awang jika tidak diaplikasikan. Yuk kita aplikasikan!
(Untuk contoh bridge pattern ini, saya ambil dari buku Design Patterns JAVA Workbook .)
Misalkan kita punya modal diagram kelas yang pada superclassnya terdapat sebuah kelas abstrak “MachineController”. Nah, di dalam MachineController ini terdapat beberapa abstract dan concrete method. Salah satu concrete method pada MachineController, yaitu inputfull, menggunakan hasil dari salah satu abstract method “getQueueMax()”.
public boolean inputfull(){
return getQueueMax() >= getQueue.size();

}
Dan selayaknya sebuah abstract method, method getQueueMax() ini baru akan didefinisikan dalam kelas yang turunan MachineController, yaitu StarPressController dan ShellController. Kedua kelas turunan ini mendefinisikan method getQueueMax() dengan cara yang berbeda. Berikut ini adalah diagram kelas yang dimaksudkan :
1
Sampai disini, masih baik-baik saja. 
Kemudian pada suatu hari, kita baru menyadari bahwa kita membutuhkan mekanisme (controller) yang berbeda pada untuk proses pengetesan.
Misalkan kita membutuhkan satu tambahan method pada kelas abstrak MachineController, yaitu method overflow(). Maka kita akan membuat sebuah kelas baru, kelas abstrak TestController, yang diturunkan dari kelas MachineController.
Sebagai kelas abstark, TestController membutuhkan StarPressController dan ShellController untuk mendefinisikan method abstrak getQueueMax(). Maka bentuk diagram kelas yang kita miliki sekarang adalah :
2
Dengan penambahan suatu mekanisme baru, maka bertambahlah tiga buah kelas pada diagram kelas kita. Bagaimana nanti jika kita akan menambahkan dua mekanisme baru? Berarti akan ada penambahan enam kelas baru! Bagaimana jika ada sepuluh mekanisme baru?! Begitu seterusnya hingga kelas-kelas ini beranak-pinak.
Keruwetan ini dapat ditangani oleh bridge pattern.
Langkah yang perlu dilakukan adalah :
  1. Buatlah sebuah interface yang berisi method-method abstrak dari superclass (MachineController).
  2. Untuk kelas-kelas yang mendefinisikan method abstrak dengan definisi yang berbeda, pindahkan letakkan dibawah interface. (implements)
  3. Kelas abstrak MachineController didefinisikan sebagai concrete class .
  4. Lakukan aggregasi! Buatlah sebuah atribut yang merupakan instans dari interface DriverController. Gunakan instans ini untuk mengakses method-method pada interface DriverController sebagai pengisi dari method konkrit, contohnya inputfull(). [Karena inputfull membutuhkan method getQueueMax()] Maka “bentuk” method inputfull() sekarang adalah :
public boolean inputfull(){
return driver.getQueueMax() <= driver.getQueue.getSize();

}
Dan diagram kelas kita sekarang adalah :
3
Nah, sekarang jika kita menginginkan mekanisme kontrol yang baru, tinggal membuat SEBUAH kelas yang diturunkan dari MachineController, semisal dengan TestController. Demikian pula jika suatu saat nanti kita membutuhkan tipe driver yang baru, maka kita tinggal menambah sebuah kelas yang mengimplementasikan DriverController, semisal dengan ShellDriver.
Inilah bridge pattern yang berhasil memisahkan (decouple) antara abstraksi dengan implementasinya. 
Dalam bukunya “Design Pattern Explained : A New Perspective on Object-Oriented Design”, Alan Shalloway dan James R. Trott menyatakan bahwa penurunan memang merupakan “surga” dalam pemrograman berbasis objek. Namun penggunaan penurunan kelas secara berlebihan akan menyulitkan pengembangan aplikasi di masa yang akan datang.

Setelah class diagram di atas, sekarang kita lanjutkan ke penerapannya pada sebuah script PHP seperti di bawah ini :
1
2 
3abstract class SaklarImplementasi {
4    abstract function status_awal($in_awal);
5    abstract function status_akhir($in_akhir);
6}
7 
8class SaklarKamar extends SaklarImplementasi {
9    function status_awal($status_awal_in) {
10      return '|||||'.$status_awal_in.'|||||';
11    }
12    function status_akhir($status_akhir_in) {
13      return '------'.$status_akhir_in.'------';
14    }
15}
16 
17class SaklarDapur extends SaklarImplementasi {
18    function status_awal($status_awal_in) {
19      return $status_awal_in;
20    }
21    function status_akhir($status_akhir_in) {
22      return strtoupper($status_akhir_in);
23    }
24}
25 
26class SaklarGarasi extends SaklarImplementasi {
27    function status_awal($status_awal_in) {
28      return 'garasi - '.$status_awal_in;
29    }
30    function status_akhir($status_akhir_in) {
31      return 'garasi - '.strtoupper($status_akhir_in);
32    }
33}
34 
35abstract class Saklar {
36    private $status_awal;
37    private $status_akhir;
38    private $kode;
39    function __construct($status_awal_in, $status_akhir_in, $kode_in) {
40      $this->status_awal = $status_awal_in;
41      $this->status_akhir  = $status_akhir_in;
42      if ($kode_in == 'dapur') {
43        $this->kode = new SaklarDapur();
44      } else if($kode_in == 'kamar') {
45        $this->kode = new SaklarKamar();
46      } else if($kode_in == 'garasi') {
47        $this->kode = new SaklarGarasi();
48      }
49    }
50    function status_awal() {
51      return $this->kode->status_awal($this->status_awal);
52    }
53    function status_akhir() {
54      return $this->kode->status_akhir($this->status_akhir);
55    }
56}
57 
58class SaklarHidup extends Saklar {
59    function hidupkan() {
60      return $this->status_awal() . " -> " . $this->status_akhir();
61    }
62
63 
64class SaklarMati extends Saklar {
65    function matikan() {
66      return $this->status_akhir() . ' -> ' . $this->status_awal();
67    }
68}
69  cetak('Lampu Kamar, mati -> hidup');
70  $saklar = new SaklarHidup('off','on','kamar');
71  cetak($saklar->hidupkan());
72  cetak('');
73 
74  cetak('Lampu Dapur, mati -> hidup');
75  $saklar = new SaklarHidup('off','on','dapur');
76  cetak($saklar->hidupkan());
77  cetak('');
78 
79  cetak('Lampu Kamar, hidup -> mati');
80  $saklar = new SaklarMati('off','on','kamar');
81  cetak($saklar->matikan());
82  cetak('');
83 
84  cetak('Lampu Dapur, hidup -> mati');
85  $saklar = new SaklarMati('off','on','dapur');
86  cetak($saklar->matikan());
87  cetak('');
88 
89  function cetak($isi) {
90    echo $isi."
"
;
91  }
92 ?>

Strategi Method Pattern

NIM : 1108100268
Strategy pattern (juga dikenal sebagai kebijakan pola) adalah pola desain perangkat lunak tertentu, dimana algoritma dapat dipilih saat runtime. Secara resmi berbicara, strategi pola mendefinisikan sebuah keluarga algoritma, merangkum masing-masing, dan membuat mereka saling dipertukarkan. Strategi ini memungkinkan algoritma yang bervariasi secara independen dari klien yang menggunakannya.[1]
Sebagai contoh, sebuah kelas yang melakukan validasi pada data yang masuk dapat menggunakan pola strategi untuk memilih algoritma validasi yang didasarkan pada jenis data, sumber data, pilihan pengguna, dan faktor-faktor lain yang membedakan, atau. Faktor-faktor ini tidak diketahui untuk setiap kasus sampai run-time, dan mungkin memerlukan validasi yang berbeda untuk dilakukan. Strategi validasi, dikemas secara terpisah dari objek memvalidasi, dapat digunakan oleh benda-benda lain memvalidasi dalam berbagai bidang sistem (atau bahkan berbagai sistem) tanpa duplikasi kode.
Persyaratan penting dalam bahasa pemrograman adalah kemampuan untuk menyimpan referensi untuk beberapa kode dalam struktur data dan mengambilnya. Ini dapat dicapai dengan mekanisme seperti pointer asli fungsi, fungsi kelas, kelas atau kelas contoh dalam berorientasi objek bahasa pemrograman, atau mengakses penyimpanan internal implementasi bahasa kode melalui refleksi.
Structure :
Contoh di Java
// The classes that implement a concrete strategy should implement this.
// The context class uses this to call the concrete strategy.
interface Strategy {
    int execute(int a, int b); 
}

// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyAdd's execute()");
        return a + b;  // Do an addition with a and b
    }
}

class ConcreteStrategySubtract implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategySubtract's execute()");
        return a - b;  // Do a subtraction with a and b
    }
}

class ConcreteStrategyMultiply implements Strategy {

    public int execute(int a, int b) {
        System.out.println("Called ConcreteStrategyMultiply's execute()");
        return a * b;   // Do a multiplication with a and b
    }    
}

// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {

    private Strategy strategy;

    // Constructor
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int a, int b) {
        return strategy.execute(a, b);
    }
}

// Test application
class StrategyExample {

    public static void main(String[] args) {

        Context context;

        // Three contexts following different strategies
        context = new Context(new ConcreteStrategyAdd());
        int resultA = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategySubtract());
        int resultB = context.executeStrategy(3,4);

        context = new Context(new ConcreteStrategyMultiply());
        int resultC = context.executeStrategy(3,4);
    }
}

Adapter Pattern

Kondisi :
Kita mempunyai sebuah class dan sebuah client yang akan menggunakan class tersebut, tetapi tipe class yang akan kita pakai tersebut tidak cocok dengan tipe yang dibutuhkan oleh client*. Yang dimaksud dengan “tipe class tidak cocok “ di sini adalah bahwa class tersebut secara hierarki tidak bisa masuk ke tipe yang dibutuhkan client. (Di akhir tulisan ini disertakan sebuah contoh kasus)
* Sebagai contoh di Java, client di sini dapat berupa sebuah method yang mempunyai parameter bertipe tertentu. Tipe parameter inilah yang tidak sesuai dengan tipe class yang sudah ada.
Tujuan :
Mengubah kontrak dari suatu class sehingga dapat memenuhi kontrak dari client.
Kontrak** di sini adalah behavior yang harus dipunyai oleh suatu class.
**Di Java, kontrak bisa diimplementasikan sebagai interface, abstract class, atau bahkan concrete class. Tapi pada desain yang baik (prefer interface rather than implementation), kontrak biasa diimplementasikan sebagai interface.
Solusi :
Cara untuk mengatasi hal ini cukup mudah, yaitu tinggal membuat sebuah class baru yang memenuhi kontrak dari client.
Implementasi :
Secara intuitif, ada dua pendekatan solusi yang bisa kita lakukan, yaitu dengan inheritance dan dengan composition. Cara yang pertama dikenal sebagai class adapter dan cara kedua dikenal sebagai object adapter.
Sebagai contoh kasus, jika kita sudah punya definisi class sebagai berikut (existing classes).

 1  interface Animal {
 2      public void sleep();
 3  }
 4
 5  class Cat implements Animal {
 6      public void sleep() {
 7          System.out.println("I'm so sleeeepy.. Miauwww");
 8          System.out.println("(Trying to find comfy place to sleep)");
 9          System.out.println("(Find a place, and put its head on its hand)");
10      }
11  }
12
13  class Mouse implements Animal {
14      public void sleep() {
15          System.out.println("Soo sleeepy.. cit cit");
16          System.out.println("(Try to find a hole..)");
17          System.out.println("(Can't find one..creating a new one)");
18          System.out.println("Zzz..zzz.z");
19      }
20  }

Permasalahan : kita harus menggunakan beberapa ekor binatang (Animal) sebagai pemeran (Actor) pada sebuah adegan film (MovieScene), karena skenario film tersebut membutuhkan adegan dimana beberapa ekor binatang akan tidur.

 1  class MovieScene {
 2
 3      private List actorList = new ArrayList();
 4
 5      public void addActor(Actor actor) {
 6          this.actorList.add(actor);
 7      }
 8
 9      public void playScene() {
10          System.out.println("--Begin the scene--");
11
12          Actor actor;
13          for (Iterator iter = actorList.iterator(); iter.hasNext();) {
14              actor = (Actor) iter.next();
15              actor.act();
16          }
17
18          System.out.println("--End fo scene--");
19      }
20
21  }
22
23
24  interface Actor {
25      public void act();
26  }
27
28  class HumanActor implements Actor {
29      public void act() {
30          System.out.println("I'm an human actor and now doing my scenario script...");
31      }
32  }
Kita mempunyai sebuah permasalahan baru, di mana kita harus menggunakan class yang sudah ada sebelumya (Cat) di dalam solusi permasalahan baru kita.
 1  class Movie {
 2      public static void main(String[] args) {
 3          MovieScene scene = new MovieScene();
 4          Actor bruceWillis = new HumanActor();
 5  
 6          scene.addActor(bruceWillis);
 7  
 8          Cat tom = new Cat();
 9          // Animal or Cat can't fit into Actor
10          // so we can't use scene.addActor(Cat cat);
11          // nor write Actor tom = new Cat() here
12          // nor using scene.addActor(Animal a); if we declare tom as an Animal
13  
14          scene.playScene();
15      }
16  }
Untuk mengatasi hal ini, kita mengimplementasikan Adapter pattern, salah satu caranya yaitu dengan membuat sebuah class baru yang meng-implement interface yang dibutuhkan oleh MovieScene.addActor (yaitu Actor), dan menghubungkan method yang dibutuhkan oleh Actor dengan method yang disediakan oleh Animal.

 1  class AnimalActor implements Actor{
 2
 3      private Animal animal;
 4
 5      public AnimalActor(Animal animal){
 6          this.animal = animal;
 7      }
 8
 9      public void act() {
10          animal.sleep();
11      }
12
13  }
Cara yang kedua adalah dengan melakukan composition antara subclass dari ActorAdapter berikut ini dengan Animal, dan meng-override method yang ada dengan pemanggilan method pada Animal.

1  class ActorAdapter implements Actor{
2      public void act() {
3          // do nothing..
4      }
5  }
Sekarang kita bisa menggunakan dua binatang kita untuk ikut serta dalam film.

 1  class MouseActor extends ActorAdapter{
 2    
 3      private Mouse mouse;
 4    
 5      public MouseActor(Mouse mouse){
 6          this.mouse = mouse;
 7      }
 8    
 9      public MouseActor(){
10          mouse.sleep();
11      }
12    
13      public void act() {
14          super.act();
15      }
16  }
17
18
19  class Movie {
20      public static void main(String[] args) {
21          MovieScene scene = new MovieScene();
22          Actor bruceWillis = new HumanActor();
23
24          Cat tom = new Cat();
25          Mouse jerry = new Mouse();
26        
27          Actor adaptedTom = new AnimalActor(tom);
28          Actor adaptedJerry = new MouseActor(jerry);
29        
30          scene.addActor(bruceWillis);
31          scene.addActor(adaptedTom);
32          scene.addActor(adaptedJerry);
33        
34          // suppose that we want to create a new type of Actor and add it to the scene
35          scene.addActor(new ActorAdapter(){
36              public void act() {
37                  System.out.println("Simply do nothing..");
38                  // hey, this looks like how we add an adapter on Swing ;)
39              }
40          });
41        
42          scene.playScene();
43      }
44  }

Composite Pattern

composite pattern. Salah satu contoh aplikasi semisal, Microsoft Office Visio memungkinkan pengguna untuk dapat membuat berbagai bentuk semisal kotak, lingkaran, dan sebagainya. Kemudian pengguna dapat melakukan grouping terhadap bentuk-bentuk yang ada dan ketika group tersebut dipindahkan posisinya, otomatis semua shape yang ada dalam group tersebut ikut berpindah posisinya. Pengguna juga dapat melakukan grouping kembali terhadap group shape yang ada dengan shape-shape yang lain sehingga dapat terbentuk semacam hierarki pada shape yang ada. Bentuk-bentuk permasalahan semacam inilah yang dapat diselesaikan dengan Composite Pattern.
Sebagai kelanjutan dari postingan Strategy Pattern, kali ini program sederhana tersebut akan dikembangkan sehingga program tersebut dapat melakukan grouping terhadap shape kotak, lingkaran dan garis. Langkah-langkah yang harus dilakukan adalah sebagai berikut:

  1. Buat sebuah class Komposisi yang merupakan group dari shape yang ada
  2. 01///
    02/// Author: Jeffrey Hermanto Halimsetiawan
    03///
    04public class Komposisi : Shape
    05{
    06    private Point _origin;
    07
    08    private List _gambar;
    09
    10    public Point Origin
    11    {
    12        get
    13        {
    14            return _origin;
    15        }
    16        set
    17        {
    18            _origin = value;
    19        }
    20    }
    21
    22    public Komposisi()
    23    {
    24        _gambar = new List();
    25    }
    26
    27    public void Add(Shape s)
    28    {
    29        _gambar.Add(s);
    30    }
    31
    32    public void Remove(Shape s)
    33    {
    34        _gambar.Remove(s);
    35    }
    36
    37    public override void Draw(System.Drawing.Graphics g)
    38    {
    39        foreach (Shape s in _gambar)
    40        {
    41            s.Draw(g, _origin);
    42        }
    43    }
    44
    45    public override void Draw(System.Drawing.Graphics g, Point origin)
    46    {
    47        Point newOrigin = new Point(origin.X + _origin.X,
    48                                    origin.Y + _origin.Y);
    49        foreach (Shape s in _gambar)
    50        {
    51            s.Draw(g, newOrigin);
    52        }
    53    }
    54}
  3. Tambahkan abstract method Draw(Graphics g, Point origin) untuk mengatasi penggambaran Komposisi dimana posisi dari shape nya adalah posisi komposisi ditambah dengan posisi relatif shape tersebut terhadap posisi (0,0) Komposisi.
  4. 1///
    2/// Author: Jeffrey Hermanto Halimsetiawan
    3///
    4public abstract class Shape
    5{
    6    public abstract void Draw(Graphics g);
    7    public abstract void Draw(Graphics g, Point origin);
    8}
  5. Implementasikan method Draw(Graphics g, Point origin) pada Kotak
  6. 01///
    02/// Author: Jeffrey Hermanto Halimsetiawan
    03///
    04public class Kotak : Shape
    05{
    06    private Point _kiriAtas;
    07    private Point _kananBawah;
    08
    09    public Point KananBawah
    10    {
    11        get
    12        {
    13            return _kananBawah;
    14        }
    15        set
    16        {
    17            _kananBawah = value;
    18        }
    19    }
    20
    21    public Point KiriAtas
    22    {
    23        get
    24        {
    25            return _kiriAtas;
    26        }
    27        set
    28        {
    29            _kiriAtas = value;
    30        }
    31    }
    32
    33    public override void Draw(System.Drawing.Graphics g)
    34    {
    35        Draw(g, new Point(0, 0));
    36    }
    37
    38    public override void Draw(System.Drawing.Graphics g, Point origin)
    39    {
    40        Pen pen = new Pen(new SolidBrush(Color.Brown));
    41
    42        Point t1 = new Point(origin.X + _kiriAtas.X,
    43                             origin.Y + _kiriAtas.Y);
    44        Point t2 = new Point(origin.X + _kananBawah.X,
    45                             origin.Y + _kananBawah.Y);
    46
    47        g.DrawRectangle(pen, t1.X,
    48                             t1.Y,
    49                             t2.X - t1.X,
    50                             t2.Y - t1.Y);
    51    }
    52}
  7. Implementasikan method Draw(Graphics g, Point origin) pada Lingkaran
  8. 01///
    02/// Author: Jeffrey Hermanto Halimsetiawan
    03///
    04public class Lingkaran : Shape
    05{
    06    private Point _pusat;
    07    private int _radius;
    08
    09    public Point Pusat
    10    {
    11        get
    12        {
    13            return _pusat;
    14        }
    15        set
    16        {
    17            _pusat = value;
    18        }
    19    }
    20
    21    public int Radius
    22    {
    23        get
    24        {
    25            return _radius;
    26        }
    27        set
    28        {
    29            _radius = value;
    30        }
    31    }
    32
    33    public override void Draw(System.Drawing.Graphics g)
    34    {
    35        Draw(g, new Point(0, 0));
    36    }
    37
    38    public override void Draw(System.Drawing.Graphics g, Point origin)
    39    {
    40        Pen pen = new Pen(new SolidBrush(Color.DarkGreen));
    41
    42        Point tp = new Point(origin.X + _pusat.X,
    43                             origin.Y + _pusat.Y);
    44
    45        g.DrawEllipse(pen, tp.X - _radius,
    46                           tp.Y - _radius,
    47                           _radius * 2,
    48                           _radius * 2);
    49    }
    50}
  9. Implementasikan method Draw(Graphics g, Point origin) pada Garis
  10. 01///
    02/// Author: Jeffrey Hermanto Halimsetiawan
    03///
    04public class Garis : Shape
    05{
    06    private Point _titik1;
    07    private Point _titik2;
    08
    09    public Point Titik1
    10    {
    11        get
    12        {
    13            return _titik1;
    14        }
    15        set
    16        {
    17            _titik1 = value;
    18        }
    19    }
    20
    21    public Point Titik2
    22    {
    23        get
    24        {
    25            return _titik2;
    26        }
    27        set
    28        {
    29            _titik2 = value;
    30        }
    31    }
    32
    33    public override void Draw(System.Drawing.Graphics g)
    34    {
    35        Draw(g, new Point(0, 0));
    36    }
    37
    38    public override void Draw(System.Drawing.Graphics g, Point origin)
    39    {
    40        Pen pen = new Pen(new SolidBrush(Color.Magenta));
    41
    42        Point t1 = new Point(origin.X + _titik1.X,
    43                             origin.Y + _titik1.Y);
    44        Point t2 = new Point(origin.X + _titik2.X,
    45                             origin.Y + _titik2.Y);
    46
    47        g.DrawLine(pen, t1, t2);
    48    }
    49}
  11. Mengubah inisialisasi Shape yang ada pada method InitializeShape() dengan memanfaatkan class Komposisi
  12. 01///
    02/// Author: Jeffrey Hermanto Halimsetiawan
    03/// Untuk melakukan inisialisasi shape yang ada
    04///
    05private void InitializeShape()
    06{
    07    Komposisi kp = new Komposisi();
    08    kp.Origin = new Point(500, 100);
    09
    10    Kotak k = new Kotak();
    11    k.KiriAtas = new Point(10, 10);
    12    k.KananBawah = new Point(110, 110);
    13    kp.Add(k);
    14
    15    Lingkaran l = new Lingkaran();
    16    l.Pusat = new Point(60, 60);
    17    l.Radius = 30;
    18    kp.Add(l);
    19
    20    Garis g = new Garis();
    21    g.Titik1 = new Point(81, 39);
    22    g.Titik2 = new Point(39, 81);
    23    kp.Add(g);
    24
    25    Komposisi kp2 = new Komposisi();
    26    kp2.Origin = new Point(10, 200);
    27    kp2.Add(k);
    28    kp2.Add(l);
    29    kp2.Add(g);
    30
    31    _gambar.Add(kp);
    32    _gambar.Add(kp2);
    33
    34}
Secara umum, class diagram dari program ini adalah sebagai berikut:
Class Diagram Simple Composite
Class Diagram Simple Composite
Output dari program di atas adalah sebagai berikut:
Output Simple Composite
Output Simple Composite
Tentunya, pattern ini akan sangat berguna sekali dalam menyelesaikan permasalahan yang hierarkial.