Anda di halaman 1dari 99

Catatan Interpreting Design Pattern by. Ahmad Hidayat Sumber: http://www.oodesign.com I. Design Principles 1.1 Pendahuluan 1.1.

1 Apa itu Software Design Principles? Software design principles merupakan kumpulan pedoman yang membantu kita untuk menghindari desain yang buruk. Prinsip-prinsip desain yang berhubungan dalam buku Robert Martin yang berjudul "Agile Software Development: Principles, Patterns, and Practices". Terdapat 3 karakteristik penting dari desain yang buruk yang harus dihindari : 1. Rigidity (Kekakuan). Sulit untuk melakukan perubahan karena setiap perubahan terlalu banyak mempengaruhi bagian lain dari sistem. 2. Fragility (kerapuhan). Ketika melakukan perubahan, dapat membuat terhentinya bagian dari sistem yang tidak kita duga. 3. Immobility. Sulit untuk digunakan kembali dalam aplikasi lain karena tidak bisa diuraikan dari aplikasi yang bersangkutan. 1.1.2 Open Close Principle Software entitas seperti class, modul dan fungsi harus terbuka untuk ekstensi (digunakan jangka panjang, by Yayat) tapi ditutup untuk modifikasi. OPC adalah prinsip generik. Anda dapat mempertimbangkan hal itu ketika menulis class Anda untuk memastikan bahwa ketika Anda perlu untuk memperpanjang perilaku mereka Anda tidak harus mengubah class namun untuk memperpanjang itu. Prinsip yang sama dapat diterapkan untuk modul, paket, perpustakaan. Jika Anda memiliki sebuah perpustakaan yang berisi satu set class ada banyak alasan yang membuat Anda lebih memilih untuk memperpanjang tanpa mengubah kode yang sudah ditulis (kompatibilitas, pengujian regresi). Ini sebabnya mengapa kita harus memastikan modul kita mengikuti Prinsip Buka Tutup (Open Close Principle). Ketika mengacu pada class, Open Close Principle dapat dipastikan dengan menggunakan abstract class dan concrete classes untuk menerapkan perilakunya. Ini akan membuat kita memiliki concrete classes untuk memperluas abstract class bukan mengubah abstract class. Contoh kasusnya adalah Template Pattern dan Strategy Pattern.

1.1.3

Dependency Inversion Principle Modul High-level tidak harus bergantung pada modul Low-level. Keduanya harus bergantung pada abstraksi.

1.1.4

1.1.5

Abstraksi tidak harus tergantung pada detail. Detail harus bergantung pada abstraksi. Dependency Inversion Principle menyatakan bahwa kita harus memisahkan modul High-level dari modul Low-level, memasukan abstraction layer di antara high level classes dan low level classes. Lebih jauh lagi tentang membalikkan ketergantungan (inverts the dependency) : daripada pada kita menulis abstraksi didasarkan pada rincian, lebih baik menulis rincian berdasarkan abstraksi. Ketergantungan Inversi atau Pembalikan dari Control (Dependency Inversion or Inversion of Control) lebih mengacu pada istilah suatu cara di mana dependensi terealisasi. Dengan cara klasik ketika software module (class, framework, dll) membutuhkan beberapa modul lain, dilakukan inisialisasi dan memegang direct reference. Ini akan membuat 2 modul yang sukar digabungkan. Dalam rangka untuk memisahkan mereka modul pertama akan memberikan sangkutan (hook) (seperti properti, parameter dll) dan sebuah modul eksternal mengontrol dependensi akan menyuntikkan (injeksi) referensi untuk yang kedua.. Dengan menerapkan Dependency Inversion (Inversi Ketergantungan), suatu modul dapat dengan mudah diubah oleh modul lain hanya dengan mengubah modul ketergantungan. Factories and Abstract Factorie dapat digunakan sebagai dependency frameworks (framework kerja ketergantungan), tetapi ada framworks khusus untuk itu, yang dikenal sebagai Inversion of Control Container. Interface Segregation Principle Clients tidak boleh dipaksa untuk bergantung pada interfaces yang tidak digunakan. Prinsip ini mengajarkan kita untuk berhati-hati bagaimana kita membuat interface. Ketika kita membuat interface, kita harus berhati-hati untuk hanya menambahkan metode yang harus ada. Jika kita menambahkan metode di dalam interface yang seharusnya tidak ada class yang mengimplementasikan interface dengan menerapkan metodenya. Sebagai contoh jika kita membuat sebuah interface yang disebut Pekerja dan menambahkan metode istirahat makan siang, semua pekerja harus menerapkannya. Bagaimana jika pekerja adalah robot? Sebagai kesimpulan interface yang berisi metode yang tidak spesifik seperti di atas disebut interface tercemar. Kita harus menghindarinya. Single Responsibility Principle Sebuah class hanya memiliki satu alasan untuk berubah. Dalam konteks ini tanggung jawab (responsibility) ini dianggap sebagai satu-satunya alasan untuk berubah. Prinsip ini menyatakan bahwa jika kita memiliki 2 alasan untuk mengubah satu class, kita harus membagi fungsi dalam dua class. Setiap class hanya akan menangani satu tanggung jawab dan jika kemudian hari kita perlu membuat satu perubahan kita akan membuatnya dalam class yang menanganinya. Ketika kita perlu melakukan

1.1.6

perubahan dalam class yang memiliki tanggung jawab yang lebih dari satu fungsi, hal ini dapat mempengaruhi fungsi lainya. Single Responsibility Principle diperkenalkan Tom DeMarco dalam bukunya yang berjudul Structured Analysis and Systems Specification, 1979. Robert Martin menafsirkan kembali konsep dan menegaskan tanggung jawab (responsibility) sebagai satu alasan untuk berubah. Liskov's Substitution Principle Tipe turunan harus benar-benar dapat menggantikan basis atau induknya. Prinsip ini hanyalah kelanjutan dari Open Close Principle dalam hal behavior, bahwa kita harus memastikan class turunan memperpanjang class dasar atau induk tanpa mengubah behaviornya. Class-class turunan yang baru harus mampu menggantikan class induk tanpa perubahan kode. Prinsip Pergantian Liskov yang diperkenalkan oleh Barbara Liskov dalam Konferensi 1987 pada Aplikasi dan Sistem Bahasa Pemrograman Berorientasi Obyek, dalam abstraksi dan hirarki data.

1.2 Pembahasan Lebih Dalam 1.2.1 Open Close Principle Motivasi Sebuah desain aplikasi dan impelentasi kode yang pandai harus memperhatikan frekuensi perubahan yang sering dilakukan selama fase pengembangan dan pemeliharaan aplikasi. Biasanya, banyak perubahan ketika sebuah fungsi baru ditambahkan ke aplikasi. Perubahan-perubahan dalam kode yang sudah ada harus diminimalkan, karena telah diasumsikan bahwa kode yang ada sudah dilakukan unit test dan perubahan kode yang sudah ditulis dapat mempengaruhi fungsi yang ada yang tidak diinginkan.

Open Close Principle menyatakan bahwa desain dan penulisan kode harus dilakukan untuk meminimalisasi perubahan kode yang sudah ada ketika fungsi baru harus ditambahkan. Desain harus dilakukan untuk memungkinkan penambahan fungsi baru sebagai class baru, menjaga sebanyak mungkin kode yang ada tidak berubah. Maksud Software entities seperti classes, modules dan functions harus terbuka untuk diperluas, tapi tertutup untuk modifikasi . Contoh Di bawah ini adalah contoh yang melanggar Open Close Principle. Implementasi sebuah editor grafis yang dapat menggambar dengan berbagai bentuk. Ini jelas bahwa ia tidak mengikuti Open Close Principle sejak class GraphicEditor harus dimodifikasi untuk setiap class bentuk baru yang harus ditambahkan. Ada beberapa kelemahan: 1. Untuk setiap penambahan bentuk baru, pengujian unit GraphicEditor harus diulang. 2. Ketika jenis bentuk baru ditambahkan waktu untuk menambahkan hal itu akan menjadi tinggi karena pengembang yang menambahkannya harus memahami logika GraphicEditor tersebut. 3. Menambahkan bentuk baru dapat mempengaruhi fungsi yang ada dengan cara yang tidak diinginkan, bahkan jika bentuk baru bekerja sempurna Dalam rangka untuk memiliki efek yang lebih dramatis, bayangkan bahwa Editor Grafis adalah class besar, dengan banyak fungsi di dalamnya, ditulis dan diubah oleh banyak pengembang, sementara bentuknya (shape) mungkin class yang diimplementasikan hanya oleh satu pengembang. Dalam hal ini akan menjadi perbaikan besar untuk memungkinkan penambahan bentuk (shape) baru tanpa mengubah class GraphicEditor.

// Open-Close Principle - Bad example

class GraphicEditor { public void drawShape(Shape s) { if (s.m_type==1) drawRectangle(s); else if (s.m_type==2) drawCircle(s); } public void drawCircle(Circle r) {....} public void drawRectangle(Rectangle r) {....} } class Shape { int m_type; } class Rectangle extends Shape { Rectangle() { super.m_type=1; } } class Circle extends Shape { Circle() { super.m_type=2; } } Di bawah ini adalah contoh yang mendukung Open Close Principle. Dalam desain baru kita menggunakan metode abstract draw() dalam GraphicEditor untuk menggambar objek, sampai berpindah implementasinya di concrete shape objects (bentuk). Menggunakan Open Close Principle masalah dari desain sebelumnya sudah dihindari, karena GraphicEditor tidak berubah ketika class shape baru ditambahkan: Tidak diperlukan unit testing. Tidak perlu memahami kode sumber dari GraphicEditor. Karena kode drawing telah dipindahkan ke concrete shape classes, itu menurunkan risiko untuk mempengaruhi functionallity yang sudah ada ketika functionallity baru ditambahkan.

// Open-Close Principle - Good example class GraphicEditor { public void drawShape(Shape s) { s.draw(); } } class Shape { abstract void draw(); } class Rectangle extends Shape { public void draw() { // draw the rectangle } } Kesimpulan Prinsip OCP hanyalah sebuah prinsip. Membuat desain yang fleksibel melibatkan tambahan waktu dan usaha yang dihabiskan untuk itu dan memperkenalkan tingkat baru abstraksi meningkatkan kompleksitas kode. Jadi prinsip ini harus diterapkan pada area yang paling mungkin untuk diubah. Ada banyak design patterns yang membantu kita untuk memperluas kode tanpa mengubahnya. Misalnya Decorator pattern membantu kita untuk mengikuti Open Close Principle. Juga Metode Factory atau Observer pattern dapat digunakan untuk merancang suatu aplikasi yang mudah untuk berubah dengan perubahan minimal dalam kode yang sudah ada.

1.2.2

Dependency Inversion Principle Motivasi Dalam sebuah aplikasi kita memiliki low level class yang melaksanakan operasi utama dan dasar, dan high level class yang merangkum (encapsulate) logika yang kompleks dan bergantung pada low level class. Sebuah cara alami menerapkan struktur seperti ini adalah kita menulis low level class, kemudian kita punya low level class untuk menulis high level class yang kompleks. Sejak high level class di dalam terminologi ini , kita terlihat sudah melakukan hal yang logis. Tapi ini bukan desain yang fleksibel. Apa yang terjadi jika kita perlu mengganti low level class? Mari kita ambil contoh klasik dari copy module yang membaca karakter dari keyboard dan mencetak ke perangkat printer. High level class yang mengandung logika adalah class Copy. Low level class adalah KeyboardReader dan PrinterWriter. Dalam desain yang buruk high level class menggunakan langsung low level class. Dalam hal ini jika kita ingin mengubah desain untuk mengarahkan output ke class FileWriter baru, kita harus mengubah class Salin. (Mari kita asumsikan bahwa itu adalah class yang sangat kompleks, dengan banyak logika dan benar-benar sulit untuk diuji). Untuk menghindari masalah tersebut kita dapat menggunakan abstraction layer di antara high level class dan low level class. Karena high level modules berisi logika yang kompleks mereka tidak harus bergantung pada low level modules dan abstraction layer baru tidak harus dibuat berdasarkan low level modules. Low level modules dibuat berdasarkan abstraction layer. Menurut prinsip ini cara merancang struktur class adalah mulai dari high level class ke low level modules: High Level Classes --> Abstraction Layer --> Low Level Classes Maksud high level modules tidak harus bergantung pada high level modules. Keduanya harus bergantung pada abstraksi. Abstraksi tidak harus tergantung pada rincian. Rincian harus bergantung pada abstraksi.

Contoh Di bawah ini adalah contoh yang melanggar Dependency Inversion Principle. Kita memiliki class Manager yang merupakan high level class, dan Worker yang merupakan low level class. Kita perlu menambahkan modul baru untuk aplikasi kita karena di perusahaan ada beberapa pekerja khusus yang baru dipekerjakan. Kita membuat class baru SuperWorker untuk ini. Mari kita berasumsi bahwa class Manager itu kompleks yang mengandung logika sangat kompleks. Dan sekarang kita harus mengubahnya dalam rangka menambahkan class baru yaitu SuperWorker. Mari kita lihat kelemahanya: Kita harus mengubah class Manager (ingat itu adalah sesuatu yang kompleks dan ini akan membutuhkan banyak waktu dan usaha). Beberapa fungsi yang sudah ada di class Manager mungkin akan terpengaruh. Unit testing harus diulang. Semua masalah tersebut akan membutuhkan banyak waktu untuk menyelesaikanya. Akan lebih sederhana jika aplikasi ini dirancang mengikuti Dependency Inversion Principle. Untuk itu kita membuat class Manager, sebuah interface IWorker dan class Worker implementasi dari interface IWorker. Ketika kita perlu menambahkan class SuperWorker yang harus kita lakukan adalah mengimplementasikan interface IWorker untuk itu. // Dependency Inversion Principle - Bad example class Worker { public void work() { // ....working } } class Manager { Worker m_worker; public void setWorker(Worker w) { m_worker=w; } public void manage() { m_worker.work(); } } class SuperWorker { public void work() { //.... working much more } }

Berikut ini adalah kode yang mendukung Dependency Inversion Principle. Dalam desain baru ini layer abstraction baru ditambahkan melalui Interface IWorker. Sekarang masalah dari kode di atas sudah diselesaikan: Class Manager tidak boleh diubah. Mengurangi resiko untuk mempengaruhi fungsionalitas yang sudah ada di class Manager. Tidak perlu mengulangi unit testing untuk class Manager. // Dependency Inversion Principle - Good example interface IWorker { public void work(); } class Worker implements IWorker{ public void work() { // ....working } } class SuperWorker implements IWorker{ public void work() { //.... working much more } } class Manager { IWorker m_worker; public void setWorker(IWorker w) { m_worker=w; } public void manage() { m_worker.work(); } } Kesimpulan Bila prinsip ini diterapkan, itu berarti high level class tidak bekerja secara langsung dengan class low level classes, tapi menggunakan interface sebagai abstract layer. Dalam kasus kreasi dalam membuat baru low level objects di dalam high level classes (jika perlukan) tidak dapat dilakukan dengan menggunakan operator new. Untuk itu, beberapa Creational design pattern dapat digunakan, seperti Factory Method, Abstract Factory, Prototype.

Template Design Pattern adalah contoh di mana prinsip DIP diterapkan. Tentu saja, dengan menggunakan prinsip ini menyiratkan upaya peningkatan dan kode yang lebih kompleks, tetapi lebih fleksibel. Prinsip ini tidak dapat diterapkan untuk setiap class atau setiap modul. Jika kita memiliki fungsi class yang lebih mungkin untuk tetap tidak berubah di masa depan ada tidak perlu menerapkan prinsip ini. 1.2.3 Interface Segregation Principle (ISP) Motivasi Ketika kita merancang sebuah aplikasi harus berhati-hati ketika membuat modul abstrak yang berisi beberapa submodul. Mengingat modul diimplementasikan oleh class, kita dapat memiliki sebuah abstraksi dari sistem dengan sebuah interface. Tetapi jika ingin memperluas aplikasi kita, menambahkan modul lain yang hanya berisi beberapa submodul dari sistem yang asli, kita dipaksa untuk mengimplementasikan interface secara penuh dan menulis beberapa metode kosong. Seperti sebuah interface yang dikenal interface lemak atau interface tercemar. Memiliki interface tercemar bukanlah solusi yang baik dan mungkin menyebabkan perilaku yang tidak tepat dalam sistem. Interface Segregation Principle menyatakan bahwa Client tidak boleh dipaksa untuk mengimplementasikan interface yang tidak digunakan. Daripada sebuah interface gemuk lebih baik beberapa interface kecil yang terkelompokan berdasarkan metode, yang masing-masing melayani satu submodule. Maksud Client tidak boleh dipaksa untuk bergantung pada interface yang tidak digunakan. Contoh Di bawah ini adalah contoh yang melanggar Interface Segregation Principle. Kita memiliki class Manager yang mewakili orang yang mengelola para pekerja. Dan kita memiliki 2 jenis pekerja yaitu pekerja rata-rata dan beberapa pekerja yang sangat efisien. Keduanya bekerja dan butuh istirahat untuk makan. Tapi sekarang beberapa robot dipekerjakan juga di perusahaan, tetapi mereka tidak makan sehingga mereka tidak perlu istirahat. Di satu sisi class Robot baru perlu untuk mengimplementasikan

interface IWorker karena robot bekerja. Di sisi lain, class Robot tidak perlu menerapkannya karena robot tidak makan. Inilah sebabnya mengapa dalam kasus ini IWorker dianggap interface tercemar. Jika kita menjaga desain ini, class Robot dipaksa untuk menerapkan metode makan. Kita bisa menulis sebuah class kosong yang tidak melakukan apa-apa (katakanlah istirahat makan 1 detik setiap hari), dan dapat mempunyai pengaruh yang tidak diinginkan dalam aplikasi (Misalnya manager akan melihat laporan makan siang yang diambil lebih dari jumlah orang yang ada). Menurut Interface Segregation Principle, desain yang fleksibel tidak akan memiliki interface tercemar. Dalam kasus ini interface IWorker harus dibagi dalam 2 interface yang berbeda. // interface segregation principle - bad example interface IWorker { public void work(); public void eat(); } class Worker implements IWorker{ public void work() { // ....working } public void eat() { // ...... eating in launch break } } class SuperWorker implements IWorker{ public void work() { //.... working much more } public void eat() { //.... eating in launch break } } class Manager { IWorker worker; public void setWorker(IWorker w) { worker=w; }

public void manage() { worker.work(); } } Kode di bawah sudah mendukung Interface Segregation Principle. Dengan memisahkan interface IWorker dalam 2 interface yang berbeda, class Robot baru tidak lagi dipaksa untuk implement metode makan. Juga jika kita membutuhkan fungsi lain untuk robot seperti charging kita membuat interface lain yaitu IRechargeble dengan metode recharge. // interface segregation principle - good example interface IWorker extends Feedable, Workable { } interface IWorkable { public void work(); } interface IFeedable{ public void eat(); } class Worker implements IWorkable, IFeedable{ public void work() { // ....working } public void eat() { //.... eating in launch break } } class Robot implements IWorkable{ public void work() { // ....working } } class SuperWorker implements IWorkable, IFeedable{ public void work() { //.... working much more } public void eat() { //.... eating in launch break } }

class Manager { Workable worker; public void setWorker(Workable w) { worker=w; } public void manage() { worker.work(); } } Kesimpulan Jika desain seperti ini sudah dilakukan interface lemak dapat dipisahkan menggunakan Adapter pattern. Seperti prinsip lainnya, Interface Segregation Principle adalah salah satu prinsip yang membutuhkan tambahan waktu dan usaha yang dihabiskan untuk menerapkannya selama waktu desain dan meningkatkan kompleksitas kode. Tapi itu menghasilkan desain yang fleksibel. Jika kita menerapkanya lebih daripada yang diperlukan, itu akan menghasilkan kode yang terdapat banyak interface dengan satu metode, sehingga penerapanya harus berdasarkan pengalaman dan akal sehat dalam mengidentifikasi daerah-daerah di mana perluasan kode lebih mungkin terjadi di masa depan. 1.2.4 Single Responsibility Principle Motivasi Dalam konteks ini responsibility (tanggung jawab) dianggap sebagai salah satu alasan untuk berubah. Prinsip ini menyatakan bahwa jika kita memiliki 2 alasan untuk mengubah sebuah class, kita harus membagi fungsionalitasnya menjadi dua class. Setiap class hanya akan menangani satu tanggung jawab dan ketika kita perlu membuat satu perubahan kita akan membuatnyandalam class yang menanganinya. Ketika kita perlu membuat perubahan dalam class yang memiliki tanggung jawab yang lebih, perubahan dapat mempengaruhi fungsi lain dari class. Single Responsibility Principle adalah prinsip sederhana dan intuitif, tetapi dalam prakteknya kadang-kadang sulit untuk bisa melakukannya dengan benar. Maksud Sebuah class harus memiliki satu alasan untuk berubah.

Contoh Mari kita asumsikan kita perlu obyek untuk melayani pesan email. Kita akan menggunakan interface IEmail dari contoh di bawah. Pada pandangan pertama semuanya terlihat baik-baik saja. Dengan melihat lebih dekat kita dapat melihat bahwa kami interface IEmail dan class Email memiliki 2 tanggung jawab (alasan untuk berubah). Yang pertama penggunaan class untuk melayani beberapa protokol email seperti pop3 atau imap. Jika protokol lain harus dapat didukung, obyek harus terserialisasi dengan cara lain dan kode harus ditambahkan untuk mendukung protokol baru. Yang kedua untuk mengelola field Konten. Bahkan jika konten adalah sebuah string mungkin kita ingin di masa depan untuk mendukung format HTML atau lainnya. Jika kita tetap hanya satu class, setiap perubahan untuk sebuah tanggung jawab mungkin akan mempengaruhi yang lain: Menambahkan sebuah protokol baru akan menciptakan kebutuhan untuk menambahkan kode untuk melakukan parsing dan serialisasi konten untuk setiap jenis field. Menambahkan jenis konten baru (seperti html) membuat kita menambahkan kode untuk setiap protokol yang sudah diimplementasikan. // single responsibility principle - bad example interface IEmail { public void setSender(String sender); public void setReceiver(String receiver); public void setContent(String content); } class Email implements IEmail { public void setSender(String sender) {// set sender; } public void setReceiver(String receiver) {// set receiver; } public void setContent(String content) {// set content; } } Kita bisa membuat interface baru dan class yang disebut IContent dan Content untuk membagi tanggung jawab. Setelah hanya satu tanggung jawab untuk masing-masing class memberi kita desain yang lebih fleksibel: menambahkan protokol baru menyebabkan perubahan hanya di class Email. menambahkan jenis konten baru yang didukung menyebabkan perubahan hanya di class Conten.

// single responsibility principle - good example interface IEmail { public void setSender(String sender); public void setReceiver(String receiver); public void setContent(IContent content); } interface IContent { public String getAsString(); // used for serialization } class Email implements IEmail { public void setSender(String sender) {// set sender; } public void setReceiver(String receiver) {// set receiver; } public void setContent(IContent content) {// set content; } } Kesimpulan Single Responsibility Principle merupakan cara yang baik untuk mengidentifikasi class selama fase desain sebuah aplikasi dan itu mengingatkan Anda untuk memikirkan semua cara class dapat berkembang. Sebuah pemisahan dari tanggung jawab yang baik ini dilakukan bila gambaran lengkap tentang bagaimana aplikasi harus bekerja dengan baik dimengerti.

1.2.5

Liskov's Substitution Principle(LSP) Motivasi Sepanjang waktu kita merancang modul program dan membuat beberapa hierarki class. Kemudian kita memperluas beberapa class, membuat beberapa classs turunan. Kita harus memastikan bahwa class turunan baru hanya diperluas tanpa mengganti fungsi lama class. Kalau tidak, class-class baru dapat menghasilkan efek yang tidak diinginkan ketika mereka digunakan dalam modul program yang ada. Likov's Substitution Principle ini menyatakan bahwa jika modul program menggunakan Base class, maka referensi ke Base class bisa diganti dengan class turunanya tanpa mempengaruhi fungsi dari modul program. Maksud Derived types harus benar-benar disubstitusikan untuk base types nya.

Contoh Di bawah ini adalah contoh klasik yang melanggar Likov's Substitution Principle. Dalam contoh ini ada 2 class yang digunakan: Rectangle dan Square. Kita asumsikan bahwa objek Rectangle digunakan di suatu tempat di dalam aplikasi. Kita memperluas aplikasi dan menambahkan class Square. Class Square dikembalikan oleh factory pattern, didasarkan pada beberapa kondisi dan kita tidak tahu persis apa jenis objek yang akan dikembalikan. Tapi kita tahu itu Rectangle. Kita mendapatkan (get) objek rectangle, mengatur (set) lebar (width) sampai 5 dan tinggi (height) sampai 10 dan mendapatkan (get) area untuk rectangle dengan lebar (width) 5 dan tinggi (height) 10 luas area nya menjadi 50. Sebaliknya, hasilnya menjadi 100 // Violation of Likov's Substitution Principle class Rectangle { protected int m_width; protected int m_height; public void setWidth(int width){ m_width = width; } public void setHeight(int height){ m_height = height; }

public int getWidth(){ return m_width; } public int getHeight(){ return m_height; } public int getArea(){ return m_width * m_height; } } class Square extends Rectangle { public void setWidth(int width){ m_width = width; m_height = width; }

public void setHeight(int height){ m_width = height; m_height = height; } } class LspTest { private static Rectangle getNewRectangle() { // it can be an object returned by some factory ... return new Square(); } public static void main (String args[]) { Rectangle r = LspTest.getNewRectangle(); r.setWidth(5); r.setHeight(10); // user knows that r it's a rectangle. // It assumes that he's able to set the width and height as for the base class System.out.println(r.getArea()); // now he's surprised to see that the area is 100 instead of 50. } } Kesimpulan Prinsip ini hanyalah perpanjangan dari Open Close Principle dan itu berarti kita harus memastikan bahwa derived (turunan) class baru memperpanjang base class tanpa mengubah perilaku mereka. II. Creational Patterns 2.1 Singleton Pattern Motivasi Kadang-kadang penting untuk memiliki hanya satu instance untuk satu class. Misalnya, dalam suatu sistem harus ada hanya satu window manager (atau hanya satu sistem file atau hanya satu print spooler). Biasanya singletons digunakan untuk manajemen sumber daya internal atau eksternal terpusat dan menyediakan akses global untuk diri mereka sendiri.

Singleton pattern merupakan salah satu design patterns paling sederhana: hanya melibatkan satu class yang responsible terhadap instance nya sendiri, untuk memastikan itu ciptakan tidak lebih dari satu instance, di saat yang sama memberikan sebuah akses global ke instance tersebut. Dalam kasus ini instance yang sama dapat digunakan dari mana-mana, karena tidak mungkin untuk memanggil konstruktor langsung setiap waktu. Maksud Memastikan bahwa hanya satu instance dari class yang dibuat. Memberikan global point untuk mengakses objek. Implementasi Implementasi melibatkan static member di dalam class "Singleton", sebuah private constructor dan sebuah static public method yang mengembalikan reference ke static member.

Singleton Pattern mendefinisikan operasi getInstance yang memperlihatkan instance unik yang diakses oleh Client. getInstance() bertanggung jawab untuk menciptakan instance class unik dalam kasus ini tidak dibuat dan untuk mengembalikan instance tersebut. class Singleton { private static Singleton instance; private Singleton() { ... } public static synchronized Singleton getInstance() {

if (instance == null) instance = new Singleton(); return instance; } ... public void doSomething() { ... } } Anda dapat melihat pada kode di atas bahwa metode getInstance memastikan hanya satu instance dari class yang dibuat. Konstruktor tidak boleh diakses dari luar class untuk memastikan satu-satunya cara instansiasi class hanya melalui metode getInstance. Metode getInstance digunakan juga untuk memberikan akses dari luar ke objek dan dapat digunakan dengan cara berikut: Singleton.getInstance().doSomething(); Contoh & Penerapan Menurut definisi singleton pattern digunakan bila harus ada tepat satu instance dari class, dan ketika itu harus dapat diakses oleh Client dari titik akses global. Berikut adalah beberapa situasi nyata di mana singleton digunakan: Contoh 1 - Logger Classes Singleton pattern digunakan dalam desain class logger. Class ini umumnya diimplementasikan sebagai singleton, dan menyediakan global logging access point dalam semua komponen aplikasi tanpa perlu untuk menciptakan sebuah objek setiap kali operasi logging dilakukan. Contoh 2 - Configuration Classes Singleton pattern digunakan untuk merancang class yang menyediakan pengaturan konfigurasi untuk aplikasi. Dengan menerapkan configuration class sebagai Singleton tidak hanya menyediakan sebuah global access point, tapi kita juga tetap menggunakan instance sebagai cache dari objek. Ketika class sudah diinstansiasi (atau ketika nilai dibaca) singleton akan menjaga nilai-nilai dalam struktur internal. Jika nilai-nilai yang dibaca dari database atau dari file ini menghindari reload nilai setiap kali parameter konfigurasi digunakan.

Contoh 3 - Accesing resources in shared mode Hal ini dapat digunakan dalam desain sebuah aplikasi yang perlu untuk bekerja dengan port serial. Mari kita mengatakan bahwa ada banyak class dalam aplikasi, bekerja di lingkungan multi-threading, yang harus melakukan operasi pada port serial. Dalam kasus ini singleton dengan synchronized methods dapat digunakan untuk mengelola semua operasi pada port serial. Contoh 4 - Factories implemented as Singletons Mari kita asumsikan merancang aplikasi dengan sebuah factory untuk generate objek baru (Acount, Customer, Site, Address) dengan id mereka, dalam lingkungan multithreading. Jika factory telah diinstansiasi dua kali dalam 2 thread yang berbeda maka memungkinkan untuk memiliki 2 id tumpang tindih untuk 2 objek yang berbeda. Jika kita menerapkan Factory sebagai singleton kita menghindari masalah ini. Menggabungkan Abstract Factory atau Factory Method dan Singleton design patterns adalah praktek yang umum. Implementasi dan Masalah Spesifik Thread-safe diimplementasikan untuk penggunaan multi-threading. Sebuah implementasi robust singleton harus dapat bekerja dalam kondisi apapun. Inilah sebabnya mengapa kita perlu memastikan untuk dapat bekerja ketika multiple threads menggunakannya. Seperti yang terlihat pada contoh sebelumnya singleton dapat digunakan khususnya dalam aplikasi multi-threaded untuk memastikan proses reads/writes tersinkronisasi. Lazy instantiation menggunakan double locking mechanism. Penerapan standar ditunjukkan dalam kode di atas adalah implementasi thread safe, tapi itu bukan penerapan terbaik thread-safe karena sinkronisasi sangat mahal ketika kita berbicara tentang kinerja. Kita bisa melihat bahwa metode getInstance sudah tersinkronisasi tidak perlu lagi diperiksa untuk sinkronisasi setelah objek diinisialisasi. Jika kita melihat bahwa objek singleton sudah dibuat kita hanya perlu mengembalikannya tanpa menggunakan syncronized block. Optimasi ini terdiri dari pemeriksaan di dalam sebuah unsynchronized block jika objek bernilai null dan jika tidak untuk diperiksa lagi dan menciptakannya dalam synchronized block. Ini disebut mekanisme penguncian ganda (double locking mechanism).

Dalam kasus ini singleton instance dibuat ketika metode getInstance () dipanggil untuk pertama kalinya. Ini disebut lazy instantiation dan memastikan bahwa singleton instance dibuat hanya bila diperlukan. //Lazy instantiation using double locking mechanism. class Singleton { private static Singleton instance; private Singleton() { System.out.println("Singleton(): Initializing Instance"); } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if (instance == null) { System.out.println("getInstance(): getInstance was invoked!"); instance = new Singleton(); } } } return instance; } public void doSomething() { System.out.println("doSomething(): Singleton does something!"); } } Diskusi lebih detail (double locking mechanism) dapat ditemukan di http://www128.ibm.com/developerworks/java/library/j-dcl.html?loc=j Awal Instansiasi menggunakan implementasi dengan static field. Dalam implementattion berikut obyek singleton diinstansiasi ketika class tersebut dimuat dan tidak ketika pertama kali digunakan, karena faktanya instance member dideklarasikan static. Inilah sebabnya mengapa kita tidak perlu untuk menyinkronkan setiap bagian dari kode dalam kasus ini. Class hanya dimuat sekali, ini menjamin uniquity dari objek.

First

time

Singleton - Contoh sederhana (java) //Early instantiation using implementation with static field. class Singleton { private static Singleton instance = new Singleton(); private Singleton() { System.out.println("Singleton(): Initializing Instance"); } public static Singleton getInstance() { return instance; } public void doSomething() { System.out.println("doSomething(): Singleton does something!"); } } Protected constructor Hal ini dimungkinkan untuk menggunakan protected constructor dalam rangka untuk memungkinkan subclassing dari singleton tersebut. Teknik ini memiliki 2 kelemahan yang membuat pewarisan singleton praktis: Pertama-tama, jika konstruktor adalah protected, itu berarti bahwa class dapat instantiated dengan memanggil konstruktor dari class lain dalam paket yang sama. Sebuah solusi untuk menghindarinya adalah dengan membuat paket terpisah untuk singleton. Kedua, untuk menggunakan class turunan semua panggilan getInstance harus diubah dalam kode yang ada dari Singleton.getInstance () untuk NewSingleton.getInstance (). Multiple singleton instances jika class dimuat oleh classloaders yang berbeda mengakses sebuah singleton. Jika sebuah class (nama yang sama, paket yang sama) yang dimuat oleh 2 classloaders yang berbeda, mereka mewakili 2 class yang berbeda di memori.

Serialization Jika class Singleton mengimplementasikan interface java.io.Serializable, ketika singleton telah diserialisasi dan kemudian deserialized lebih dari sekali, ini akan ada multiple instances Singleton yang dibuat. Untuk menghindari hal ini metode

readResolve harus diimplementasikan. readResolve() dalam javadocs.

Lihat

Method

Serializable()

dan

public class Singleton implements Serializable { ... // This method is called immediately after an object of this class is deserialized. // This method returns the singleton instance. protected Object readResolve() { return getInstance(); } }

Abstract Factory dan Factory Methods diimplementasikan sebagai singleton. Ada situasi tertentu ketika sebuah factory harus unik. Memiliki 2 factory mungkin memiliki efek yang tidak diinginkan ketika objek diciptakan. Untuk memastikan factory yang unik, maka harus diimplementasikan sebagai singleton. Dengan demikian kita juga menghindari untuk instansiasi class sebelum menggunakannya. Hot Spot: Multithreading - Sebuah perawatan khusus harus diambil ketika singleton harus digunakan dalam aplikasi multithreading. Serialization - Ketika singleton mengimplementasikan interface Serializable harus implementasikan metode readResolve untuk menghindari memiliki 2 objek yang berbeda. Classloaders - Jika class Singleton diload oleh 2 class loader yang berbeda maka akan memiliki 2 class yang berbeda, satu untuk setiap class loader. Global Access Point represented by the class name Instance singleton diperoleh dengan menggunakan nama class. Pada pandangan pertama terlihat sebuah cara yang mudah untuk mengaksesnya, tetapi sangat tidak fleksibel. Jika kita perlu mengganti class Sigleton, semua references dalam kode harus diubah untuk menyesuaikan. 2.2 Factory Pattern Motivasi Factory Design Pattern mungkin adalah pattern desain yang paling banyak digunakan dalam bahasa pemrograman modern seperti Java dan C #. Muncul dalam berbagai varian dan implementasi. Jika Anda mencari tentang itu,

kemungkinan besar, Anda akan menemukan referensi tentang GoF patterns: Factory Method dan Abstract Factory. Dalam artikel ini akan menggambarkan sebuah rasa dari factory pattern yang umum digunakan saat ini. Anda juga dapat mengecek original Factory Method yang sangat mirip. Maksud menciptakan objek tanpa memaparkan logika Instansiasi ke Client. mengacu pada objek yang baru dibuat melalui interface umum Implementasi

Implementasi yang sangat sederhana Client membutuhkan Product, tapi bukannya menciptakan langsung menggunakan operator new, ia meminta obyek factory untuk Product baru, memberikan informasi tentang jenis objek yang dibutuhkan. Factory menginstansiasi sebuah new concrete product dan kemudian mengembalikan ke Client the newly created product (menolak abstract product class). Client menggunakan Product sebagai Product abstrak tanpa mengetahui tentang implementasi konkret mereka. Contoh & Penerapan Mungkin factory pattern merupakan salah satu pattern yang paling sering digunakan.

Misalnya aplikasi grafis bekerja dengan shapes. Dalam implementasi kita drawing framework adalah client dan shapes adalah product. Semua shapes berasal dari abstract shape class (atau interface). Class Shape mendefinisikan operasi draw dan move yang harus diimplentasikan oleh concrete shapes. Mari kita asumsikan perintah dipilih dari menu untuk membuat lingkaran baru. Framework tersebut menerima shape type sebagai parameter string, itu meminta factory untuk menciptakan shape baru mengirimkan parameter yang diterima dari menu. Factory menciptakan circle baru dan mengembalikannya ke framework, dilempar dengan bentuk abstrak. Kemudian framework menggunakan objek sebagai lemparan ke class abstrak tanpa menyadari jenis objek concret. Keuntungannya jelas: bentuk baru dapat ditambahkan tanpa mengubah satu baris pun kode dalam framework (kode Client yang menggunakan bentuk dari factory). Seperti yang terlihat pada bagian selanjutnya, ada implementasi factory tertentu yang memungkinkan penambahan Product baru tanpa memodifikasi class factory.

Implementasi dan Masalah Spesifik

Procedural Solution - switch/case noob instantiation

Mereka juga dikenal sebagai parameterized Factories. Menghasilkan metode yang dapat ditulis sehingga dapat menghasilkan lebih banyak jenis obyek Product, menggunakan pengkondisian (dengan masukan sebagai parameter metode atau membaca dari beberapa parameter konfigurasi global - lihat pattern factory abstract pattern) untuk mengidentifikasi jenis objek yang harus dibuat , sebagai berikut: public class ProductFactory{ public Product createProduct(String ProductID){ if (id==ID1) return new OneProduct();

if (id==ID2) return return new AnotherProduct(); ... // so on for the other Ids return null; //if the id doesn't have any of the expected values } ... } Implementasi ini adalah yang paling sederhana dan intuitif (Mari kita sebut saja noob implementation (implementasi pemula)). Disini masalahnya adalah sekali kita tambahkan concrete product call kita harus memodifikasi class factory. Hal ini sangat tidak fleksibel dan melanggar open close principle. Tentu saja kita bisa membuat subclass dari factory class, tetapi jangan lupa bahwa class factory biasanya digunakan sebagai singleton. Subclassing itu berarti mengganti semua referensi class factory di mana-mana melalui kode. Class Registration - avoiding reflection Jika Anda dapat menggunakan refleksi, misalnya di Java atau bahasa .NET, Anda dapat mendaftarkan class product baru ke factory tanpa mengubah factory itu sendiri. Untuk membuat objek di dalam class factory tanpa mengetahui jenis objek kita membuat sebuah pemetaan antara productID dan jenis class product. Dalam kasus ini ketika sebuah product baru ditambahkan ke aplikasi itu harus didaftarkan ke factory. Operasi ini tidak memerlukan perubahan kode class factory. class ProductFactory { private HashMap m_RegisteredProducts = new HashMap(); public void registerProduct (String productID, Class productClass) { m_RegisteredProducts.put(productID, productClass); } public Product createProduct(String productID) { Class productClass = (Class)m_RegisteredProducts.get(productID); Constructor productConstructor = cClass.getDeclaredConstructor(new Class[] { String.class }); return (Product)productConstructor.newInstance(new Object[] { }); } } Kita bisa menempatkan kode registrasi di manapun dalam kode kita, tapi tempat yang cocok adalah di dalam class product dalam konstruktor statis. Lihatlah contoh di bawah ini:

1. Pendaftaran dilakukan di luar class Product: public static void main(String args[]){ Factory.instance().registerProduct("ID1", OneProduct.class); } 2. Pendaftaran dilakukan di dalam class Product: class OneProduct extends Product { static { Factory.instance().registerProduct("ID1",OneProduct.class); } ... } Kita harus memastikan bahwa concrete product classes sudah dimuat sebelum mereka diminta oleh factory untuk pendaftaran (jika mereka tidak dimuat mereka tidak akan didaftarkan di factory dan createProduct akan mengembalikan nilai null). Untuk memastikan itu kita akan menggunakan metode Class.forName tepat di bagian static dari main class. Bagian ini dijalankan tepat setelah main class di-load. Class.forName seharusnya mengembalikan instance class dari class yang ditunjukkan. Jika class tidak dimuat oleh kompilator sama sekali, itu akan dimuat saat Class.forName ini dipanggil. Akibatnya blok static di class masing-masing akan dieksekusi ketika masing-masing class dimuat: class Main { static { try { Class.forName("OneProduct"); Class.forName("AnotherProduct"); } catch (ClassNotFoundException any) { any.printStackTrace(); } } public static void PhoneCallNotRegisteredException { ... } } main(String args[]) throws

Implementasi refleksi memiliki kelemahan tersendiri. Yang utama adalah kinerja. Ketika refleksi digunakan kinerja pada kode yang melibatkan refleksi dapat menurunkan sampai 10% dari poerfomance dari kode non reflection. Masalah lainnya adalah bahwa tidak semua bahasa pemrograman menyediakan mekanisme refleksi. Class Registration - avoiding reflection Seperti kita lihat dalam paragraf sebelumnya obyek factory menggunakan internal HashMap untuk membuat pemetaan antara parameter (dalam kasus kita adalah Strings) dan concrete products class. Pendaftaran dibuat dari luar factory karena objek diciptakan menggunakan refleksi factory yang tidak mengetahui jenis objectnya. Kita tidak ingin menggunakan refleksi tapi di saat yang sama kita ingin memiliki mengurangi coupling antara factory dan concrete products. Sejak factory harus tidak mengetahui product, kita harus memindahkan penciptaan object di luar factory untuk mengetahui object dari concrete product classes. Itu akan menjadi concrete class nya sendiri. Kita menambahkan metode abstrak baru di class product abstrak. Setiap class concrete akan menerapkan metode ini untuk membuat objek baru dari jenis yang sama seperti dirinya sendiri. Kami juga harus mengubah metode registration tersebut bahwa kita akan meregistrasi obyek Product concrete bukan objek class. abstract class Product { public abstract Product createProduct(); ... } class OneProduct extends Product { ... static { ProductFactory.instance().registerProduct("ID1", OneProduct()); } public OneProduct createProduct() { return new OneProduct(); } ... }

new

class ProductFactory { public void registerProduct(String productID, Product p) m_RegisteredProducts.put(productID, p); } public Product createProduct(String productID){

((Product)m_RegisteredProducts.get(productID)).createProduct(); } } A more advanced solution - Factory design pattern with abstractions (Factory Method)

Implementasi ini merupakan alternatif untuk impelentasi class registration. Mari kita asumsikan kita perlu menambahkan Product baru ke aplikasi. Untuk implementasi switch-case prosedural kita perlu mengubah class Factory, sedangkan dalam implementasi class registration, kita butuh untuk mendaftarkan class untuk factory tanpa memodifikasi class factory sedikitpun. Untuk meyakinkan ini adalah solusi yang fleksibel. Implementasi prosedural adalah contoh kasil yang buruk untuk Open-Close Principle. Seperti yang bisa kita lihat ada solusi yang paling intuitif untuk menghindari memodifikasi class Factory untuk memperluasnya.

Ini adalah implementasi klasik dari factory method pattern. Ada beberapa kekurangan atas implementasi class registration dan tidak beigut banyak keuntungan: + derived factory method dapat diubah untuk melakukan operasi tambahan ketika objek diciptakan (mungkin beberapa inisialisasi berdasarkan beberapa parameter global ...). - Factory tidak dapat digunakan sebagai singleton. - Setiap factory harus diinisialisasi sebelum menggunakannya. - Lebih sulit untuk diterapkan. - Jika objek baru harus ditambahkan sebuah factory baru harus diciptakan. Pokoknya, implementasi klasik ini memiliki keuntungan bahwa itu akan membantu kita memahami Abstract Factory design pattern. Kesimpulan Bila Anda merancang aplikasi hanya berpikir jika Anda benar-benar membutuhkan sebuah factory untuk membuat objek. Mungkin menggunakannya akan membawa kompleksitas yang tidak perlu dalam aplikasi Anda. Jika Anda memiliki banyak object dari base type yang sama dan Anda memanipulasi mereka sebagian besar casted to abstract types, maka Anda perlu sebuah factory. Jika Anda harus memiliki banyak kode seperti berikut kode ini, Anda harus mempertimbangkan kembali itu: (if (ConcreteProduct)genericProduct typeof ) ((ConcreteProduct)genericProduct).doSomeConcreteOperation(); Jika Anda memutuskan menggunakan factory, saya akan merekomendasikan menggunakan salah satu class registration implementations (dengan atau tanpa refleksi) dan untuk menghindari Metode Factory (Factory design pattern dengan abstractions). Harap dicatat implementasi switch-case prosedural (noob) adalah paling sederhana, melanggar prinsip OCP hanya digunakan untuk menjelaskan teori. Satu-satunya cara bijaksana untuk menggunakannya adalah untuk modul sementara sampai diganti dengan factory yang nyata. 2.3 Factory Method Pattern Motivasi Dikenal juga sebagai Virtual Constructor, Factory Method berkaitan dengan gagasan di mana libraries bekerja: library menggunakan abstract class untuk mendefinisikan dan memelihara relasi antara objek. Salah satu jenis responsibility nya adalah membuat object. Library diketahui ketika suatu objek perlu dibuat,

tetapi tidak tahu jenis dari objek yang harus membuat, ini menjadi spesifik untuk aplikasi menggunakan library. Factory method bekerja dengan cara yang sama: ia mendefinisikan sebuah interface untuk menciptakan objek, tetapi membuat pilihan dari tipenya pada subclass, penciptaan yang ditangguhkan pada saat run-time. Sebuah contoh kehidupan nyata sederhana Metode Factory adalah hotel. Ketika ingin tinggal di sebuah hotel pertama Anda harus check in. Orang yang bekerja di front-desk akan memberi Anda kunci untuk ruangan Anda setelah Anda membayar untuk ruangan yang Anda inginkan dan cara ini ia dapat dipandang sebagai sebuah ruang factory. Sementara tinggal di hotel, Anda mungkin perlu untuk melakukan panggilan telepon, sehingga Anda menelepon meja depan dan orang di sana akan menghubungkan Anda dengan nomor yang Anda butuhkan, menjadi sebuah panggilan telepon factory, karena ia mengontrol akses ke panggilan , juga. Maksud Mendefinisikan sebuah interface untuk menciptakan objek, tetapi membiarkan subclass untuk menentukan class untuk instansiasi Mengacu pada objek yang baru dibuat melalui interface umum Implementasi Pattern dasarnya bekerja seperti yang ditunjukkan di bawah ini, dalam diagram UML:

Class peserta dalam pattern ini adalah: Product mendefinisikan interface untuk objek yang dibuat metode factory. ConcreteProduct mengimplementasikan interface Product. Creator (juga disebut sebagai factory karena ia menciptakan obyek Product) menyatakan metode FactoryMethod, yang mengembalikan objek Product. Mungkin memanggil generating metode untuk membuat objek Product ConcreteCreator overrides the generating method untuk membuat objek ConcreteProduct Semua concrete products adalah subclass dari class Product, sehingga semua dari mereka memiliki implementasi dasar yang sama, pada batas tertentu. Class Creator menentukan semua behavior standar dan generik dari Product dan ketika sebuah Product baru dibutuhkan, ia akan mengirimkan rincian penciptaan yang disediakan oleh Client untuk ConcreteCreator tersebut. Setelah diagram ini sudah di dalam pikiran, mudah bagi kita sekarang untuk menghasilkan kode yang berhubungan dengan itu. Berikut adalah bagaimana implementasi metode factory klasik akan terlihat: public interface Product { } public abstract class Creator { public void anOperation() { Product product = factoryMethod(); } protected abstract Product factoryMethod(); } public class ConcreteProduct implements Product { } public class ConcreteCreator extends Creator { protected Product factoryMethod() { return new ConcreteProduct(); } } public class Client { public static void main( String arg[] ) { Creator creator = new ConcreteCreator(); creator.anOperation(); }

} Contoh dan Penerapan Kebutuhan untuk menerapkan Metode Factory sangat sering. Kasus nya seperti salah satunya di bawah ini: ketika class tidak dapat mengetahui lebih dulu tipe dari objek ingin dibuat ketika sebuah class ingin subclass untuk menjadi satu-satunya tipe spesifik dari objek yang baru dibuat

Contoh 1 - Document Application. Mempertimbangkan framework untuk aplikasi desktop. Aplikasi ini dimaksudkan untuk bekerja dengan document. Sebuah framework untuk aplikasi desktop terdapat definisi untuk operasi seperti opening, creating dan saving sebuah document. Class-class dasar yang abstrak, bernama Application dan Document, Client mereka harus membuat subclass dari mereka dalam rangka untuk menentukan aplikasi mereka sendiri. Untuk menghasilkan sebuah aplikasi menggambar, misalnya, mereka harus menentukan class DrawingApplication dan DrawingDocument. Class Aplication mempunyai tugas mengelola document, mengambil tindakan atas permintaan Client (misalnya, ketika pengguna memilih perintah open atau save dari menu). Karena class Document perlu untuk diinstansiasi untuk aplikasi yang spesifik, class Application tidak tahu itu sebelumnya, sehingga tidak tahu apa yang harus diinstansiasi, tidak tahu kapan harus instantiate itu. Framework ini perlu instantiate class tertentu, tetapi hanya tahu class abstrak yang tidak dapat instantiated. Factory Method design pattern memecahkan masalah dengan menempatkan semua informasi yang berkaitan dengan class yang perlu instantiated menjadi objek dan menggunakan mereka di luar framework, seperti yang Anda lihat di bawah ini

Dalam class Application metode CreateDocument baik memiliki implementasi default atau tidak memiliki implementasi sama sekali, operasi ini sedang didefinisikan ulang dalam subclass MyApplication sehingga membuat objek MyDocument dan mengembalikan referensi untuk itu. public Document CreateDocument(String type){ if (type.isEqual("html")) return new HtmlDocument(); if (type.isEqual("proprietary")) return new MyDocument(); if (type.isEqual("pdf")) return new PdfDocument (); } Asumsikan class Application memiliki anggota disebut docs yang merupakan list dari document yang ditangani oleh aplikasi, maka metode NewDocument akan terlihat seperti ini: public void NewDocument(String type){ Document doc=CreateDocument(type); Docs.add(doc); Docs.Open(); } Metode ini akan diwariskan oleh class MyApplication dan, sehingga, melalui metode CreateDocument, itu benar-benar akan instantiate objek MyDocument. Kita akan memanggil metode CreateDocument sebuah Method Factory karena bertanggung jawab dengan 'membuat' objek. Melalui metode ini, didefinisikan kembali dalam subclass Aplikasi, kita benar-benar dapat membentuk situasi di mana class Application menciptakan objek tanpa mengetahui jenis mereka. Dari sudut pandang ini metode factory adalah pattern yang memberikan kita cara untuk mencapai DIP principle. Implementasi dan Spesifik Masalah Ketika mengimplementasi Factory Method design pattern beberapa masalah yang dapat muncul: Definisi dari Creator class Jika kita menerapkan pattern ini ke kode yang sudah ditulis mungkin ada masalah dengan cara kita memiliki Creator class sudah ditetapkan. Ada dua kasus: class Creator adalah abstrak dan mengenerate metode yang tidak memiliki implementasi apapun. Dalam hal ini class ConcreteCreator harus mendefinisikan metode generasi mereka sendiri dan situasi ini biasanya

muncul dalam kasus-kasus di mana class Creator tidak dapat memprediksi apa yang akan diinstantiate ConcreteProduct. Class Creator adalah class concrete, menghasilkan metode yang memiliki implementasi default. Jika ini terjadi, class ConcreteCreator akan menggunakan generating metode untuk fleksibilitas ketimbang untuk generasi. Programmer akan selalu dapat memodifikasi class objek yang class Creator implisit menciptakan, mendefinisikan ulang metode generasi.

Metode Factory hanya kasus tertentu dari factory design pattern. Pada saat yang sama itu adalah factory pattern yang paling dikenal, mungkin karena diterbitkan di GoF. Dalam bahasa pemrograman modern factory dengan registration lebih digunakan. Kekurangan dan Manfaat Berikut adalah manfaat dan kelemahan dari factory method pattern: + Alasan utama factory pattern yang digunakan adalah bahwa ia memperkenalkan pemisahan antara aplikasi dan keluarga class (memperkenalkan kopling lemah bukan kopling ketat menyembunyikan class konkret dari aplikasi). Ini menyediakan cara sederhana untuk memperluas keluarga products dengan perubahan kecil dalam kode aplikasi. + Ini menyediakan kait kustomisasi. Jika objek yang dibuat secara langsung di dalam class sulit untuk menggantikan mereka dengan objek yang memperpanjang fungsi mereka. Daripada factory digunakan untuk menciptakan sebuah keluarga objek, customisasi objek dapat dengan mudah menggantikan objek aslinya, konfigurasikan factory untuk menciptakan mereka. - Factory harus digunakan untuk keluarga object. Jika class tidak memperpanjang common base class atau interface mereka tidak dapat digunakan dalam template desain factory. Hot Poin: Metode factory adalah salah satu yang paling digunakan dan salah satu desain pattern yang lebih kuat. Hanya ada beberapa poin yang harus dipertimbangkan ketika Anda menerapkan metode factory. Bila Anda merancang aplikasi hanya berpikir jika Anda benar-benar membutuhkannya sebuah factory untuk membuat objek. Mungkin menggunakannya akan membawa kompleksitas yang tidak perlu dalam aplikasi Anda. Namun demikian, jika Anda memiliki banyak objek dari tipe dasar yang sama dan Anda memanipulasi mereka kebanyakan sebagai obyek abstrak, maka Anda perlu sebuah factory. Code anda harus punya banyak kode seperti berikut, pertimbangkan ini.

if (genericProduct typeof ConcreteProduct) ((ConcreteProduct)genericProduct).doSomeConcreteOperation(); 2.4 Abstract Factory Motivasi Modularisasi adalah isu besar dalam pemrograman saat ini. Programmer di seluruh dunia sedang mencoba untuk menghindari gagasan menambahkan kode ke class yang ada dalam rangka untuk membuat mereka mendukung encapsulasi informasi yang lebih umum. Ambil kasus seorang manajer informasi yang mengelola nomor telepon. Nomor telepon memiliki aturan tertentu di mana mereka bisa dihasilkan tergantung pada wilayah dan state. Jika di beberapa titik aplikasi harus diubah dalam rangka mendukung menambahkan angka membentuk state baru, kode aplikasi harus diubah dan itu akan menjadi lebih rumit. Untuk mencegah hal itu, Abstract Factory design pattern digunakan. Menggunakan pattern ini, sebuah framework didefinisikan, yang menghasilkan objek-objek yang mengikuti pattern umum dan pada saat runtime factory ini dipasangkan dengan concrete factory untuk memProductsi objek-objek yang mengikuti pattern dari tempat tertentu. Dengan kata lain, Abstract Factory adalah super-factory yang menciptakan factories lain (factory of factories). Maksud Abstract Factory menawarkan interface untuk menciptakan sebuah keluarga obyek yang terkait, tanpa secara eksplisit menentukan class mereka. Implementasi Pattern basically bekerja seperti yang ditunjukkan di bawah ini, dalam diagram UML:

Class-class yang berpartisipasi Abstract Factory pattern di atas adalah: AbstractFactory - mendeklarasikan interface untuk operasi yang menciptakan abstract products. ConcreteFactory - mengimplementasikan operasi untuk menciptakan concrete products. AbstractProduct - mendeklarasikan sebuah interface untuk jenis objek product. Product - mendefinisikan Product yang akan dibuat oleh ConcreteFactory yang sesuai, menerapkan interface AbstractProduct. Client - menggunakan interface yang telah dideklarasikan oleh class AbstractFactory dan AbstractProduct. Class AbstractFactory adalah salah satu yang menentukan jenis sebenarnya dari concrete object dan menciptakannya, tetapi mengembalikan sebuah abstract pointer ke concrete object yang baru saja dibuat. Ini menentukan perilaku client yang meminta factory untuk membuat sebuah objek dari tipe abstrak tertentu dan mengembalikan pointer abstrak untuk itu, menjaga client dari mengetahui apa-apa tentang penciptaan sebenarnya dari objek. Fakta bahwa factory mengembalikan sebuah abstract pointer ke objek yang telah diciptakan, berarti bahwa client tidak memiliki pengetahuan tentang objek type nya. Ini berarti bahwa tidak ada kebutuhan untuk including deklarasi class apapun yang berkaitan dengan concrete type, client berurusan setiap saat dengan abstract type. Obyek dari concrete type yang dibuat oleh factory, diakses oleh client hanya melalui interface abstrak. Implikasi kedua dari cara membuat objek adalah ketika menambahkan concrete type baru dibutuhkan, yang harus kita lakukan adalah memodifikasi kode client dan membuatnya menggunakan factory yang berbeda, yang jauh lebih mudah daripada instantiating type baru, yang mengharuskan mengubah kode di manapun objek baru dibuat. Impelentasi klasik untuk Abstract Factory pattern adalah sebagai berikut: abstract class AbstractProductA{ public abstract void operationA1(); public abstract void operationA2(); } class ProductA1 extends AbstractProductA{ ProductA1(String arg){ System.out.println("Hello "+arg); } // Implement the code here public void operationA1() { }; public void operationA2() { }; }

class ProductA2 extends AbstractProductA{ ProductA2(String arg){ System.out.println("Hello "+arg); } // Implement the code here public void operationA1() { }; public void operationA2() { }; } abstract class AbstractProductB{ //public abstract void operationB1(); //public abstract void operationB2(); } class ProductB1 extends AbstractProductB{ ProductB1(String arg){ System.out.println("Hello "+arg); } // Implement the code here } class ProductB2 extends AbstractProductB{ ProductB2(String arg){ System.out.println("Hello "+arg); } // Implement the code here } abstract class AbstractFactory{ abstract AbstractProductA createProductA(); abstract AbstractProductB createProductB(); } class ConcreteFactory1 extends AbstractFactory{ AbstractProductA createProductA(){ return new ProductA1("ProductA1"); } AbstractProductB createProductB(){ return new ProductB1("ProductB1"); } } class ConcreteFactory2 extends AbstractFactory{ AbstractProductA createProductA(){ return new ProductA2("ProductA2"); } AbstractProductB createProductB(){ return new ProductB2("ProductB2"); } } //Factory creator - an indirect way of instantiating the factories class FactoryMaker{

private static AbstractFactory pf=null; static AbstractFactory getFactory(String choice){ if(choice.equals("a")){ pf=new ConcreteFactory1(); }else if(choice.equals("b")){ pf=new ConcreteFactory2(); } return pf; } } // Client public class Client{ public static void main(String args[]){ AbstractFactory pf=FactoryMaker.getFactory("a"); AbstractProductA product=pf.createProductA(); //more function calls on product } } Penerapan & Contoh Kita harus menggunakan Abstract Factory design pattern ketika: sistem harus independen dari cara kerja product yang dibuat. sistem ini atau harus dikonfigurasi untuk bekerja dengan multiple families of products. keluarga product dirancang hanya untuk bekerja bersama-sama. creation dari sebuah perpustakaan product dibutuhkan, yang relevan hanya interface, bukan implementasi, juga. Contoh Phone Number Contoh pada awal artikel dapat ditambahkan dengan alamat, juga. Class AbstractFactory akan berisi metode untuk membuat entri baru dalam information manager untuk nomor telepon dan alamat, metode yang menghasilkan product abstract Alamat dan PhoneNumber, yang milik AbstractProduct class. Class-class AbstractProduct akan mendefinisikan metode product ini untuk mendukung: untuk alamat get dan set metode untuk jalan, kota, daerah dan anggota kode pos dan nomor telepon get dan set metode untuk nomor. Class-class ConcreteFactory dan ConcreteProduct akan mengimplementasikan interface yang didefinisikan di atas dan akan muncul dalam contoh kita dalam bentuk class USAddressFactory dan class USAddress dan USPhoneNumber. Untuk setiap state baru yang perlu ditambahkan ke aplikasi, satu set baru class concrete-type akan ditambahkan. Dengan cara ini kita dapat memiliki EnglandAddressFactory, EnglandAddress dan EnglandPhoneNumber yang semuanya adalah file untuk informasi alamat Inggris.

Contoh Pizza Factory Contoh lain, kali ini lebih sederhana dan mudah dimengerti, adalah salah satu pizza factory, yang mendefinisikan nama metode dan return types untuk membuat berbagai jenis pizza. Abstrak factory dapat diberi nama AbstractPizzaFactory, RomeConcretePizzaFactory dan MilanConcretePizzaFactory menjadi dua ekstensi dari class abstrak. Abstrak factory akan menentukan jenis topping untuk pizza, seperti pepperoni, sosis atau ikan teri, dan beberapa concrete factory akan mengimplementasikan hanya satu set topping, dengan area yang spesifik dan bahkan jika salah satu topping diimplementasikan di kedua concrete factory, pizza yang dihasilkan akan subclass yang berbeda, masing-masing untuk area yang sudah diimplementasikan di dalamnya. Contoh Look & Feel Look & Feel Abstract Factory adalah contoh yang paling umum. Misalnya, framework GUI harus mendukung beberapa look and feel tema, seperti Motif dan Windows look. Gaya masing-masing mendefinisikan look dan behavior yang berbeda untuk setiap jenis control: Buttons dan Edit box. Untuk menghindari hardociding untuk setiap jenis kontrol kita mendefinisikan sebuah abstrak LookAndFeel. Panggilan ini akan instantiate, tergantung pada parameter konfigurasi dalam aplikasi dari concrete factories: WindowsLookAndFeel atau MotifLookAndFeel. Setiap permintaan untuk objek baru akan didelegasikan ke instatiated concrete factory yang akan mengembalikan control dengan jenis yang spesifik.

Implementasi dan Masalah Spesifik Abstract Factory pattern memiliki manfaat dan kekurangan. Di satu sisi, mengisolasi penciptaan objek-objek dari client yang membutuhkan mereka, client hanya diberi kemungkinan untuk mengakses mereka melalui interface, yang membuat manipulasi lebih mudah. Menukar keluarga product lebih mudah, sebagai concrete factory yang muncul hanya dalam kode dimana ia instantiated. Juga jika product dari keluarga dimaksudkan untuk bekerja sama, Abstract Factory memudahkan untuk menggunakan object-object hanya dari satu keluarga pada suatu waktu. Di sisi lain, menambahkan product baru ke factory yang sudah ada itu sulit, karena interface factory abstract menggunakan seperangkat product fix yang dapat dibuat. Itulah mengapa menambahkan Product baru berarti memperluas interface factory, yang melibatkan perubahan di class AbstractFactory dan semua subclass. Bagian ini akan membahas cara-cara menerapkan pattern untuk menghindari masalah yang mungkin muncul. Factories sebagai singletons Sebuah aplikasi biasanya membutuhkan hanya satu instance dari class ConcreteFactory per product keluarga. Ini berarti bahwa yang terbaik adalah menerapkannya sebagai Singleton. Menciptakan Product Class AbstractFactory hanya menyatakan interface untuk menciptakan Product. Ini adalah tugas dari class ConcreteProduct untuk benar-benar menciptakan Product. Untuk setiap keluarga, ide terbaik adalah menerapkan Factory Method design pattern. Sebuah concrete factory akan menentukan Productnya dengan masingmasing mengoverride metode factory. Bahkan jika implementasinya mungkin tampak sederhana, menggunakan ide ini berarti mendefinisikan subclass concrete factory baru untuk setiap product keluarga, bahkan jika class serupa dalam sebagian besar aspek. Untuk menyederhanakan kode dan meningkatkan kinerja Prototype design pattern dapat digunakan sebagai pengganti Metode Factory, terutama ketika ada banyak keluarga Product. Dalam hal ini concrete factory diawali dengan prototypical instance dari setiap Product dalam keluarga dan ketika yang baru diperlukan bukannya menciptakan itu, Prototype yang ada di cloning. Pendekatan ini menghilangkan kebutuhan untuk concrete factory baru untuk setiap keluarga baru dari product.

Memperluas factories Pengoperasian perubahan factory dalam rangka untuk itu untuk mendukung terciptanya Product baru tidak mudah. Apa yang dapat dilakukan untuk mengatasi masalah ini adalah, bukan metode CreateProduct untuk setiap Product, menggunakan sebuah Create method tunggal yang mengambil parameter yang mengidentifikasi jenis Product yang dibutuhkan. Pendekatan ini lebih fleksibel, tapi kurang aman. Masalahnya adalah bahwa semua obyek yang dikembalikan oleh Create method akan memiliki interface yang sama, yaitu salah satu yang sesuai dengan jenis dikembalikan oleh Create method dan Client tidak akan selalu dapat mendeteksi dengan benar untuk class dengan instance yang benar-benar miliknya. Hot Poin: Class AbstractFactory dideklarasikan hanya sebagai interface untuk menciptakan product. Penciptaan yang sebenarnya adalah tugas class ConcreteProduct, di mana pendekatan yang baik adalah menerapkan Factory Methode design pattern untuk setiap Product dari keluarga. Memperluas factories dapat dilakukan dengan menggunakan satu metode Create untuk semua Product dan melampirkan informasi tentang jenis Product yang dibutuhkan. 2.5 Builder Pattern Motivasi Aplikasi yang lebih kompleks adalah kompleksitas class dan objek yang digunakan meningkat. Objek yang kompleks terbuat dari bagian-bagian yang dihasilkan oleh objek-objek lain yang membutuhkan perawatan khusus ketika sedang dibangun. Sebuah aplikasi mungkin perlu sebuah mekanisme untuk membangun objek yang kompleks yang independen dari yang membentuk objeknya. Jika ini adalah masalah yang Anda sedang dihadapkan dengan, Anda mungkin ingin mencoba menggunakan Builder (atau Adaptive Builder) design pattern. Pattern ini memungkinkan objek client untuk membangun sebuah obyek yang kompleks dengan hanya menetapkan jenis dan isi, yang terlindung dari rincian yang terkait dengan representasi objek. Dengan cara ini proses konstruksi dapat digunakan untuk membuat representasi yang berbeda. Logika proses ini adalah form terisolasi langkah-langkah yang sebenarnya digunakan dalam menciptakan obyek yang kompleks, sehingga proses dapat digunakan kembali untuk membuat form objek yang berbeda, set yang sama dari obyek sederhana seperti yang pertama.

Maksud Mendefinisikan instance untuk menciptakan objek tetapi membiarkan subclass memutuskan class mana yang di instantiate Mengacu pada objek yang baru dibuat melalui interface umum Implementasi Builder design pattern menggunakan Factory Builder pattern untuk menentukan class concrete untuk memulai untuk membangun tipe objek yang diinginkan, seperti yang akan kita lihat di bawah dalam diagram UML:

Class yang berpartisipasi dalam pattern ini adalah: The Builder class menentukan sebuah interface abstrak untuk membuat bagian-bagian dari obyek Product. The ConcreteBuilder membangun dan menempatkan bersama-sama bagian dari Product dengan mengimplementasi interface Builder. Ini mendefinisikan dan melacak representasi itu menciptakan dan menyediakan sebuah interface untuk menyimpan product. Direktur class mengkonstruksi obyek yang kompleks dengan menggunakan interface Builder. The Product merupakan obyek yang kompleks yang sedang dibangun. Client, yang mungkin berupa object lain atau client aktual yang memanggil main() metode aplikasi, inisiasi class Builder dan Director. Builder merupakan obyek yang kompleks yang perlu dibangun dalam object dan type yang lebih sederhana. Konstruktor di class Director menerima objek Builder sebagai parameter dari Client dan bertanggung jawab untuk memanggil metode yang sesuai dari class Builder. Dalam rangka menyediakan Client dengan interface untuk semua concrete Builders, class Builder harus menjadi salah satu abstrak. Dengan cara ini Anda dapat menambahkan jenis baru dari obyek yang kompleks dengan hanya

mendefinisikan struktur dan menggunakan kembali logika untuk proses konstruksi yang sebenarnya. Client adalah satu-satunya yang perlu tahu tentang jenis baru, Director perlu mengetahui metode Builder yang dipanggil. Contoh berikut ini membahas kasus aplikasi mengkonversi teks:

Client perlu mengkonversi document dari format RTF ke format ASCII. Untuk itu, dia akan memanggil createASCIIText metode yang mengambil sebagai parameter document yang akan dikonversi. Metode ini panggilan concrete builder, ASCIIConverter, yang meng-extends Builder, TextConverter, dan meng-override dua metode untuk mengubah karakter dan paragraf, dan juga Director, RTFReader, yang mem-parsing document dan memanggil metode builder tergantung pada tipe Token yang ditemui. Product, ASCIIText, dibangun langkah demi langkah, dengan menambahkan karakter yang dikonversi. //Abstract Builder class abstract class TextConverter{ abstract void convertCharacter(char c); abstract void convertParagraph(); } // Product class ASCIIText{ public void append(char c){ //Implement the code here } } //Concrete Builder class ASCIIConverter extends TextConverter{ ASCIIText asciiTextObj;//resulting product /*converts a character to target representation and appends to the resulting*/ object void convertCharacter(char c){ char asciiChar = new Character(c).charValue(); //gets the ascii character

asciiTextObj.append(asciiChar); } void convertParagraph(){} ASCIIText getResult(){ return asciiTextObj; } } //This class abstracts the document object class Document{ static int value; char token; public char getNextToken(){ //Get the next token return token; } } //Director class RTFReader{ private static final char EOF='0'; //Delimitor for End of File final char CHAR='c'; final char PARA='p'; char t; TextConverter builder; RTFReader(TextConverter obj){ builder=obj; } void parseRTF(Document doc){ while ((t=doc.getNextToken())!= EOF){ switch (t){ case CHAR: builder.convertCharacter(t); case PARA: builder.convertParagraph(); } } } } //Client public class Client{ void createASCIIText(Document doc){ ASCIIConverter asciiBuilder = new ASCIIConverter(); RTFReader rtfReader = new RTFReader(asciiBuilder); rtfReader.parseRTF(doc); ASCIIText asciiText = asciiBuilder.getResult(); } public static void main(String args[]){ Client client=new Client(); Document doc=new Document(); client.createASCIIText(doc);

system.out.println("This is an example of Builder Pattern"); } } Contoh & Penerapan Builder Pattern digunakan ketika: Membuat algoritma dari obyek yang kompleks yang independen dari bagian yang benar-benar membentuk objek Sistem perlu untuk memungkinkan representasi yang berbeda untuk objek yang sedang dibangun Contoh 1 - Vehicle Manufacturer. Mari kita ambil kasus vehicle manufacturer, dari satu set bagian, dapat membangun sebuah mobil, sepeda, sepeda motor atau skuter. Dalam hal ini Builder akan menjadi VehicleBuilder tersebut. VehicleBuilder menentukan interface untuk membangun salah satu kendaraan dalam daftar di atas, dengan menggunakan set dari bagian yang sama dan seperangkat rule yang berbeda untuk setiap jenis kendaraan. ConcreteBuilders akan menjadi builders yang attached pada setiap objects yang sedang dalam pembangunan. Product ini tentu saja vehicle yang sedang dibangun dan Direktur adalah manufacturer dan toko. Contoh 1 - Students Exams. Jika kita memiliki sebuah aplikasi yang dapat digunakan oleh para mahasiswa dari Universitas untuk menyediakan daftar nilai ujian mereka, aplikasi ini perlu dijalankan dengan cara yang berbeda tergantung pada pengguna yang menggunakannya, pengguna tersebut yang harus log in. Ini berarti bahwa misalnya, admin harus memiliki beberapa tombol diaktifkan, tombol yang perlu dinonaktifkan untuk mahasiswa, pengguna umum. Builder menyediakan interface untuk membangun form tergantung pada informasi login. Para ConcreteBuilders adalah bentuk spesifik untuk setiap jenis pengguna. Product ini adalah form akhir bahwa aplikasi akan digunakan dalam kasus tertentu dan Direktur adalah aplikasi yang, berdasarkan informasi login, membutuhkan form yang spesifik. masalah Spesifik dan implementasi Builder dan Abstrak Factory Builder design pattern sangat mirip, pada batas tertentu, dengan Abstract Factory pattern. Itulah mengapa penting untuk dapat membuat perbedaan antara situasi ketika satu atau yang lain digunakan. Dalam kasus Abstract Factory, Client menggunakan metode factorys untuk membuat objek sendiri. Dalam kasus

Builders, class Builder yang diinstruksikan tentang cara membuat objek dan kemudian diminta untuk membuatnya, tapi cara class disatukan terserah class Builder, detail ini membuat perbedaan antara two patterns.

Common interface for products Dalam prakteknya Product yang diciptakan oleh concrete builders memiliki struktur yang berbeda, jadi jika tidak ada alasan untuk memperoleh Product yang berbeda dari sebuah class induk yang sama. Hal ini juga membedakan pattern Builder dari pattern Factory Abstrak yang menciptakan object-object yang berasal dari jenis umum. 2.6 Prototype Pattern Motivasi Pemrograman saat ini adalah semua tentang biaya. Saving merupakan masalah besar ketika datang untuk menggunakan sumber daya komputer, sehingga programmer melakukan yang terbaik untuk menemukan cara meningkatkan kinerja Ketika kita berbicara tentang pembuatan obyek kita dapat menemukan cara yang lebih baik untuk memiliki objek baru: cloning. Untuk ide ini satu pattern desain tertentu terkait: daripada penciptaan menggunakan clonning. Kita mengcloning objek, jika cost untuk menciptakan objek baru itu besar dan penciptaan adalah sumber daya yang intensif. Prototype design pattern merupakan satu pertanyaan. Hal ini memungkinkan sebuah objek untuk membuat customized objects tanpa mengetahui class mereka atau rincian tentang bagaimana untuk menciptakan mereka. Sampai poin ini kedengarannya sangat mirip dengan Metode Factory pattern, perbedaannya the fact that for the Factory the palette of prototypical objects tidak pernah mengandung lebih dari satu objek. Maksud menentukan jenis objek yang dibuat menggunakan prototypical instance menciptakan objek baru dengan menyalin Prototype ini Implementasi Pattern ini menggunakan class abstrak, seperti yang kita lihat di bawah dan hanya ada tiga jenis class membuat implementasinya agak mudah.

Class-class yang berpartisipasi dengan Prototype Pattern adalah: Client - menciptakan objek baru dengan meminta Prototype untuk mengcloning dirinya sendiri. Prototype - menyatakan sebuah interface untuk cloning itu sendiri. ConcretePrototype - mengimplementasikan operasi untuk cloning itu sendiri. Proses cloning dimulai ketika class diinisialisasi dan diinstantiated. Client meminta objek baru dari tipenya dan mengirimkan permintaan ke class Prototype. Sebuah ConcretePrototype, tergantung dari jenis objek yang diperlukan, akan menangani cloning melalui Clone() metode, membuat instance baru dari dirinya sendiri. Berikut adalah contoh kode untuk Prototype Pattern: public interface Prototype { public abstract Object clone ( ); } public class ConcretePrototype implements Prototype { public Object clone() { return super.clone(); } } public class Client { public static void main( String arg[] ) { ConcretePrototype obj1= new ConcretePrototype (); ConcretePrototype obj2 = ConcretePrototype)obj1.clone(); } }

Contoh ini agak sepele, tetapi penggunaan nyata dari pattern muncul saat kita tidak tahu apa yang kita sebenarnya cloning. Sebagai contoh jika kita membutuhkan objek yang baru dibuat untuk disimpan dalam hashtable kita dapat menggunakannya seperti ini: // Violation of Likov's Substitution Principle class Rectangle { protected int m_width; protected int m_height; public void setWidth(int width){ m_width = width; } public void setHeight(int height){ m_height = height; }

public int getWidth(){ return m_width; } public int getHeight(){ return m_height; } public int getArea(){ return m_width * m_height; } } class Square extends Rectangle { public void setWidth(int width){ m_width = width; m_height = width; } public void setHeight(int height){ m_width = height; m_height = height; }

} class LspTest { private static Rectangle getNewRectangle() { // it can be an object returned by some factory ... return new Square(); } public static void main (String args[]) { Rectangle r = LspTest.getNewRectangle(); r.setWidth(5); r.setHeight(10); // user knows that r it's a rectangle. // It assumes that he's able to set the width and height as for the base class System.out.println(r.getArea()); // now he's surprised to see that the area is 100 instead of 50. } } Penerapan & Contoh Gunakan Prototype Pattern ketika sistem harus independen dari bagaimana Product itu created, composed, dan represented. dan: Class yang akan diinstansiasi ditentukan pada saat run-time Diperlukan untuk menghindari penciptaan hirarki factory Lebih mudah untuk menyalin instance yang ada daripada membuat yang baru. Contoh 1 Dalam membangun stages untuk permainan yang menggunakan labirin dan objectobject visual yang berbeda bertemu karakter diperlukan metode cepat menghasilkan haze map dengan menggunakan objek-objek yang sama: wall, door, passage, room... Prototype Pattern berguna dalam kasus ini karena bukan hard coding (menggunakan operasi baru) obyek kamar, pintu, dan objek dinding yang bisa instantiated, metode CreateMaze akan parameterized oleh berbagai objek kamar, pintu, dinding dan objek jalan, sehingga komposisi peta dapat dengan mudah diubah dengan mengganti objek prototipikal dengan yang berbeda.

Client adalah metode CreateMaze dan class ConcretePrototype akan menjadi orang-orang membuat salinan untuk objek yang berbeda. Contoh 2: Misalkan kita melakukan analisis penjualan pada set data dari database. Biasanya, kita akan menyalin informasi dari database, merangkum itu menjadi obyek dan melakukan analisis. Tetapi jika analisis lain diperlukan pada set data yang sama, membaca database lagi dan menciptakan objek baru bukanlah ide yang terbaik. Jika kita menggunakan pattern Prototype maka obyek yang digunakan dalam analisis pertama akan dicloning dan digunakan untuk analisis lainnya. Client di sini salah satu metode yang memproses obyek yang merangkum informasi dari database. Class-class ConcretePrototype yang akan menjadi class, dari objek yang sudah dibuat setelah penggalian data dari database, akan disalin ke obyek yang digunakan untuk analisis. Spesifik masalah dan implementasi Menggunakan prototype manager Ketika aplikasi menggunakan banyak Prototype yang dapat diciptakan dan dihancurkan secara dinamis, registry Prototype yang tersedia harus dijaga. Registri ini disebut manajer Prototype dan harus melaksanakan operasi untuk mengelola Prototype terdaftar seperti mendaftarkan Prototype di bawah tombol tertentu, mencari Prototype dengan kunci yang diberikan, menghapus salah satu dari register, dll. Client akan menggunakan interface Prototype Manager untuk menangani Prototype pada saat run-time dan akan meminta izin sebelum menggunakan Clone() method. Tidak ada banyak perbedaan antara pelaksanaan Prototype yang menggunakan manajer Prototype dan metode factory diimplementasikan menggunakan mekanisme registrasi class. Mungkin satu-satunya perbedaan terdapat dalam kinerja. Melaksanakan operasi Clone Sebuah diskusi kecil muncul ketika berbicara tentang seberapa dalam atau dangkal tiruan harus: a deep clone clones the instance variables in the cloning object while a shallow clone shares the instance variables between the clone and the original. Usually, a shallow clone is enough and very simple, but cloning complex prototypes should use deep clones so the clone and the original are independent, a

deep clone needing its components to be the clones of the complex objects components. Initializing klon Ada kasus ketika internal states dari clone harus diinisialisasi setelah dibuat. Hal ini terjadi karena nilai-nilai ini tidak dapat dikirimkan ke metode Clone(), yang menggunakan interface yang akan didestroy jika parameter tersebut digunakan. Dalam hal ini inisialisasi harus dilakukan dengan menggunakan pengaturan dan pengaturan ulang operasi dari class Prototype atau dengan menggunakan metode menginisialisasi yang mengambil sebagai parameter nilai-nilai di mana internal state clone itu harus diset. Hot poin Prototype Manager - diimplementasikan biasanya sebagai hashtable menjaga objek untuk mengcloning. Ketika menggunakannya, Prototype menjadi metode factory yang menggunakan cloning bukan Instansiasi. Deep Clones vs. Shallow Clones - ketika kita mengcloning objek kompleks yang berisi objek-objek lain, kita harus berhati-hati bagaimana mereka dicloning. Kita bisa mengcloning yang terkandung objek juga (deep cloning) atau kita dapat referensi yang sama bagi mereka, dan untuk berbagi antara cloned container objects. Initializing Internal States - ada situasi tertentu ketika object perlu diinisialisasi setelah mereka diciptakan. 2.7 Obyek Pool Motivasi Performa kadang dapat menjadi isu utama selama pengembangan perangkat lunak dan penciptaan objek (Instansiasi kelas) merupakan langkah mahal. Sementara Prototype pattern membantu dalam meningkatkan kinerja dengan kloning objek, Object Pool pattern menawarkan mekanisme untuk menggunakan kembali objekobjek yang mahal untuk dibuat. Clients of an object pull "feel" like they are owners of a service although the service is shared among many other clients. Intent -reuse and share objects that are expensive to create.

Implementasi

Implementasinya melibatkan object-object berikut: Reusable - Wraps the limited resource, will be shared by several clients for a limited amount of time. Client - uses an instance of type Reusable. ReusablePool - manage the reusable objects for use by Clients, creating and managing a pool of objects. Ketika Client meminta sebuah objek Reusable, pool melakukan tindakan berikut: Search for an available Reusable object and if it was found it will be returned to the client. If no Reusable object was found then it tries to create a new one. If this actions succeds the new Reusable object will be returned to the client. If the pool was unable to create a new Reusable, the pool will wait until a reusable object will be re leased. The Client is responsible to request the Reusable object as well to release it to the pool. If this action will not be performed the Reusable object will be lost, being considered unavailable by the ResourcePool. The clients are not aware that they are sharing the Reusable object. From the client poinf of view they are the owners of a new object which comes from the Resource pool in the same way that it comes from a factory or another creational design pattern. The only difference is that the Client should mark the Reusable object as available, after it finishes to use it. It's not about releasing the objects; for example if we work with databases, when a connection is closed it's not necesarely distroyed, it means that it can be reused by another client.

Mengapa menggunakannya? Pada dasarnya, kita akan menggunakan object pool setiap kali ada beberapa Client yang membutuhkan the same stateless resource yang mahal untuk dibuat. Penerapan & Contoh Mari kita mengambil contoh koneksi database. Ini jelas yang membuka terlalu banyak koneksi dapat mempengaruhi kinerja karena beberapa alasan: Menciptakan connection adalah operasi yang mahal. Ketika ada terlalu banyak koneksi dibuka waktu lebih lama untuk membuat yang baru dan server database akan menjadi kelebihan beban. Here the object pool comes in to picture to manage the connections and provide a way to reuse and share them. It can also limit the maximum number of objects that can be created.

This pattern provide the following mechaninsm: Connection - represent the object which is instantiated by the client. From the client perspective this object is instantiated and it handles the database operations and it is the only object visible to the client. The client is not aware that it uses some shared connections. Internally this class does not contain any code for connecting to the database and calls ConnectionPool.aquireImpl to get a ConnectionImpl object and then delegates the request to ConnectionImpl. ConnectionImpl is the object which implements the database operations which are exposed by Connection for the client. ConnectionPool is the main actor to manage the connections to the database. It keeps a list of ConnectionImpl objects and instantiates new objects if this is required.

When the client needs to query the database it instantiate a new Connection object specifing the database name and the call the query method which returns a set of records. From the client point of view this is all. When the Connection.Query methd is called it asks for a ConnectionImpl object from the ConnectionPool. The ConnectionPool tries to find and return an unused object and if it doesn't find it creates one. At this point the maximum number of connections can be limited and if it was reached the pool cand wait until one will be available or return null. In the query method the request is delegated to the ConnectionImpl object returned by the object pool. Since the request is just delegated it's recomended to have the same method signature in Connection and ConnectionImpl. Specific problems and implementation Singleton reusable pool - The reusable pool is implemented as a singleton. The reusable pool should be accesible only to the Connection object. 1. Limited number of resources in the pool The connection pool is responsable for sharing and reusing the resources. Sometimes the resources have to be well managed only because they affects the performace, but there are situations when the number of resources can not exceed a specific limit. In this case the Resource pool check the number of instantiated resources and of the limit is reach it will wait for a resource to be released, it will throw an exception or it will return a null value. In any of the last 2 situations the Client should be notified that the action failed because there are no available resources. 2. Handling situations when creating a new resource fails There are many reasons when the ResourcePool.acquireConnectionImpl method fails to return a resource. It might happens because there are not available resources or some exception occured. Either way the client should be notified about his. 3. Sinkronisasi In order to work in a multithreading environment the methods that are used by differnt threads should be synchronized. There are only three methonds in the ResourcePool object that have to be synchronized: - getInstance should be synchronized or should contain a synchronized block. For details check the singleton multithreading implementation.

acquireConnectionImpl - this menthod returns a resource and should be synchronized not to return the same resource to two different clients running tin different threads. releaseConnectionImpl - this method release a resource. Ussually it doesn't have to be synchronized a resource is allocated only by one client. Internally some blocks might need to be synchronized(depending on the method implementation and the internal structures used to keep the pool.).

4. Sumber daya Expired (tidak terpakai tapi masih milik) Masalah utama untuk Pola Pool Object adalah bahwa obyek harus dibebaskan oleh Client ketika selesai menggunakan mereka. Ada banyak contoh ketika Client "lupa" untuk melepaskan sumber daya. Mari kita ambil contoh yang koneksi database ketika koneksi tidak ditutup / dibebaskan setelah mereka digunakan. Hal ini tampaknya masalah kecil tetapi ada banyak aplikasi menghancurkan untuk alasan ini. Di pool objek dapat menerapkan mekanisme untuk memeriksa ketika sumber daya tertentu digunakan terakhir kali dan jika waktu berakhir, untuk mengembalikannya ke pool sumber daya yang tersedia. Hot Poin Bila pola Pool Obyek digunakan obyek harus ditandai sebagai tersedia (dirilis) oleh Client setelah mereka digunakan, sehingga pool akan menyadari tentang hal ini. Ini adalah kelemahan utama karena Client harus melakukan ini dan itu adalah situasi umum ketika koneksi database tidak dirilis setelah mereka digunakan. Untuk mengatasi hal ini mekanisme dapat diimplementasikan untuk melepaskan sumber daya jika mereka tidak digunakan untuk jangka waktu. Menciptakan sumber daya akan gagal dan kasus ini harus ditangani dengan hati-hati. Ketika tidak ada sumber daya yang tersedia (beacause jumlah terbatas atau membuat yang baru gagal) Client harus diberitahu tentang hal itu.

Kesimpulan Althrough pool obyek menangani Instansiasi objek itu tujuan utama adalah untuk menyediakan cara untuk Client untuk menggunakan kembali objek seperti mereka adalah objek baru, tanpa dibagi dan digunakan kembali.

III.

Behavioral Patterns 3.1. Chain of Responsibility Motivasi Dalam menulis aplikasi apapun, sering terjadi bahwa event yang dihasilkan oleh satu objek harus ditangani oleh yang lain. And, to make our work even harder, we also happen to be denied access to the object which needs to handle the

event. In this case there are two possibilities: there is the beginner/lazy approach of making everything public, creating reference to every object and continuing from there and then there is the expert approach of using the Chain of Responsibility. Rantai pola Tanggung Jawab desain memungkinkan objek untuk mengirim perintah tanpa mengetahui apa yang objek akan menerima dan menanganinya. Permintaan dikirim dari satu objek ke yang lain membuat mereka bagian dari rantai dan setiap objek dalam rantai ini dapat menangani perintah, menyebarkannya atau melakukan keduanya. Contoh yang paling umum dari mesin menggunakan Rantai Tanggung jawab adalah slot mesin koin vending: daripada memiliki slot untuk setiap jenis koin, mesin hanya memiliki satu slot untuk mereka semua. Koin menjatuhkan diarahkan ke tempat penyimpanan yang sesuai yang ditentukan oleh penerima perintah. Maksud: It avoids attaching the sender of a request to its receiver, giving this way other objects the possibility of handling the request too. The objects become parts of a chain and the request is sent from one object to another across the chain until one of the objects will handle it. Diagram UML dari kelas bawah ini akan membantu kita lebih memahami cara Rantai bekerja.

Beberapa penjelasan tentang role dari setiap class dalam diagram di atas: Handler - defines an interface for handling requests RequestHandler - handles the requests it is responsible for if it can handle the request it does so, otherwise it sends the request to its successor Client - sends commands to the first object in the chain that may handle the command.

Here is how sending a request works in the application using the Chain of Responsibility: the Client in need of a request to be handled sends it to the chain of handlers, which are classes that extend the Handler class. Each of the handlers in the chain takes its turn at trying to handle the request it receives from the client. If ConcreteHandler_i can handle it, then the request is handled, if not it is sent to the handler ConcreteHandler_i+1, the next one in the chain. The classic example of the Chain of Responsibility's implementation is presented for us below: public class Request { private int m_value; private String m_description; public Request(String description, int value) { m_description = description; m_value = value; } public int getValue() { return m_value; } public String getDescription() { return m_description; } } public abstract class Handler { protected Handler m_successor; public void setSuccessor(Handler successor) { m_successor = successor; } public abstract void handleRequest(Request request); } public class ConcreteHandlerOne extends Handler {

public void handleRequest(Request request) { if (request.getValue() < 0) { //if request is eligible handle it System.out.println("Negative values are handled by ConcreteHandlerOne:"); System.out.println("\tConcreteHandlerOne.HandleRequest : " + request.getDescription() + request.getValue()); } else { super.handleRequest(request); } } } public class ConcreteHandlerThree extends Handler { public void handleRequest(Request request) { if (request.getValue() >= 0) { //if request is eligible handle it System.out.println("Zero values are handled by ConcreteHandlerThree:"); System.out.println("\tConcreteHandlerThree.HandleRequest : " + request.getDescription() + request.getValue()); } else { super.handleRequest(request); } } } public class ConcreteHandlerTwo extends Handler { public void handleRequest(Request request) { if (request.getValue() > 0) { //if request is eligible handle it System.out.println("Positive values ConcreteHandlerTwo:");

are

handled

by

System.out.println("\tConcreteHandlerTwo.HandleRequest : " + request.getDescription() + request.getValue()); } else { super.handleRequest(request); } } } public class Main { public static void main(String[] args) { // Setup Chain of Responsibility Handler h1 = new ConcreteHandlerOne(); Handler h2 = new ConcreteHandlerTwo(); Handler h3 = new ConcreteHandlerThree(); h1.setSuccessor(h2); h2.setSuccessor(h3); // Send requests to the chain h1.handleRequest(new Request("Negative Value ", -1)); h1.handleRequest(new Request("Negative Value ", 0)); h1.handleRequest(new Request("Negative Value ", 1)); h1.handleRequest(new Request("Negative Value ", 2)); h1.handleRequest(new Request("Negative Value ", -5)); } } Penerapan & Contoh Having so many design patterns to choose from when writing an application, it's hard to decide on which one to use, so here are a few situations when using the Chain of Responsibility is more effective: More than one object can handle a command The handler is not known in advance The handler should be determined automatically Its wished that the request is addressed to a group of objects without explicitly specifying its receiver The group of objects that may handle the command must be specified in a dynamic way

Here are some real situations in which the Chain of Responsibility is used: Example 1 In designing the software for a system that approves the purchasing requests. In this case, the values of purchase are divided into categories, each having its own approval authority. The approval authority for a given value could change at any time and the system should be flexible enough to handle the situation. The Client in the example above is the system in need of the answer to the approval. It sends a request about it to an purchase approval authority. Depending on the value of the purchase, this authority may approve the request or forward it to the next authority in the chain. For example lets say a request is placed for the purchase of a new keyboard for an office. The value of the purchase is not that big, so the request is sent from the head of the office to the head of the department and then to the materials department where it stops, being handled locally. But if equipment for the whole department is needed then the request goes form the head of the department, to materials department, to the purchase office and even to the manager if the value is too big. Example 2 In designing the software that uses a set of GUI classes where it is needed to propagate GUI events from one object to another. When an event, such as the pressing of a key or the click of the mouse, the event is needed to be sent to the object that has generated it and also to the object or objects that will handle it. The Client is, of course, the object that has generated the event, the request is the event and the handlers are the objects that can handle it. So, if we have a handler for the click of the mouse, a handler for the pressing of the Enter key and a handler for the pressing of the Delete key, that is the chain of handlers that take care of the events that are generated. Example 3 In designing a shipping system for electronic orders. The steps to complete and handle the order differs form one order to another based on the customer, the size of the order, the way of shipment, destination and

more other reasons. The business logic changes also as special cases appear, needing the system to be able to handle all cases. The Client, the electronic order in process, requests shipping based on a set of pieces of information. Its request is turned by the system into a specific form, combining the steps to completing and the details of handling, based on the input information. The system will send this type of request through a chain of orderhandlers until the input information that it comes with matches the input the order-handles takes. When special cases appear, all that is needed is a new handler to be added in the chain.

Specific problems and implementation The classic implementation of the Chain of Responsibility is just the first step in applying the pattern to our own application. Improvements based on the type of commands we are handling are needed, in order to make the use of this pattern effective. Representing requests In real life each handler represents a system. And each system can handle specific requests or requests common to more handlers. We should take this issue in consideration when we implement this pattern. In the classical samples of the CoR found on the net you can see that the request is generally represented by an integer. Of course in real life we can not use primary data types as a request. A clever design should be a flexible one. The best solution here is to create an interface a super class Request (or and interface) where to the default behavior. Then if we need to add a new handler and a specific request all we need is to extend the Request base class. Of course this is not the only approach. Lets consider the shipping system example. Each request will have to contain a large amount of data. Creating request examples for this might be difficult. We can take some xml objects containing the data, generated during the application flow (lets assume we already have the code implemented for that) and pass them to each handler. Or since the data was already saved in the database (lets assume that also) we can pass only the ids of the involved objects and then each handler will take the data required from db. Unhandled requests

Unfortunately, the Chain doesn't guarantee that every command is handled, which makes the problem worse, since unhandled commands propagate through the full length of the chain, slowing down the application. One way to solve this is by checking if, at the end of the chain, the request has been handled at least once, otherwise we will have to implement handlers for all the possible requests that may appear. Broken Chain Sometimes we could forget to include in the implementation of the handleRequest method the call to the successor, causing a break in the chain. The request isnt sent forward from the broken link and so it ends up unhandled. A variation of the pattern can be made to send the request to all the handlers by removing the condition from the handler and always calling the successor. The following implementation eliminates the Broken Chain problem. The implementation moves the code to traverse the chain into the base class keeping the request handling in a different method in the subclasses. The handleRequest method is declared as final in the base class and is responsible to traverse the chain. Each Handler have to implement the handleRequestImpl method, declared as abstract in the super class. public abstract class Handler{ private Handler m_successor; public void setSuccessor(Handler successor) { m_successor = successor; } protected abstract boolean handleRequestImpl(Request request); public final void handleRequest(Request request) { boolean handledByThisNode = this.handleRequestImpl(request); if (m_successor != null && !handledByThisNode) { m_successor.handleRequest(request); } } } protected boolean handleRequestImpl(Request request) {

if (request.getValue() < 0) { //if request is eligible handle it System.out.println("Negative values are handled by ConcreteHandlerOne:"); System.out.println("\tConcreteHandlerOne.HandleRequest : " + request.getDescription() + request.getValue()); return true; } else { return false; } } The above implementation not only that eliminates the broken chain problem, but it also offers an increased level of flexibility. Only by changing the handleRequest method we can change the pattern to send to al handlers regardless the request handling: public final void handleRequest(Request request) { boolean handledByThisNode = this.handleRequestImpl(request); if (m_successor != null && !handledByThisNode) m_successor.handleRequest(request); } Avoiding spam requests For example, an improvement that we could find useful is avoiding sending spam commands. This way, the concrete extension of the HandleRequest function will look like this: public void HandleRequest(int request) { if(isSpam(request)) { // if the request is spam take spam-related actions ... } else { // request is not spam. super.HandleRequest(request); // Pass message to next filter in the chain. } } Use on existing code

The last, but not least problem that the Chain of Responsibility creates to a programmer is the fact that it is impossible to introduce the pattern into the existing classes without modifying the source code and, even in the case where the pattern is already included in the code, if new operations need to be added to the Handler, it is impossible to do that without modifying the source code. So the basic idea is to decide from the start on whether to use the pattern or not and if we do, what methods we need. Hot points The fundamental flaw of the pattern is the fact that it gets easily broken: if the programmer forgets to call the next handler in the concreteHandler the request gets lost on the way. This problem comes from the fact that the execution is not handled entirely by the superclass and the call is triggered in the superclass. When implementing the CoR pattern a special care should be taken for the request representation. The request is not considered a distinctive part of the CoR pattern, but it is still used in all the components of the pattern. Another flaw of the Chain of Responsibility is the fact that some requests may end up unhandled due to the wrong implementation of concrete handler, their propagation slowing down the rest of the application. This means that extra care is needed when taking into account the requests that may appear in the process.. 3.2. Command Pattern "Sebuah obyek yang berisi simbol, nama atau kunci yang mewakili daftar command, tindakan atau penekanan tombol". Ini adalah definisi makro, salah satu yang harus akrab/dikenal bagi setiap pengguna komputer. Dari ide ini pola desain command tercipta/telah lahir. Makro mewakili, pada batas tertentu, sebuah command yang dibangun dari kumpulan seperangkat command lainnya, dalam urutan tertentu. Sama seperti makro, pola desain Command merangkum/encapsulate command (metode panggilan) pada objek yang memungkinkan kita untuk mengeluarkan permintaan tanpa mengetahui operasi yang diminta atau meminta object. pola Command desain menyediakan pilihan untuk command antrian (queue), undo / redo actions dan manipulasi lainnya.

Maksudnya adalah:

- Merangkum permintaan dalam suatu objek.

- memungkinkan parameterisasi klien dengan permintaan yang berbeda. - memungkinkan menyimpan permintaan dalam sebuah antrian (queue).

Implementasi Gagasan (ide) dan pelaksanaan (implementasi) pola desain Command cukup sederhana, seperti yang akan kita lihat dalam diagram di bawah ini, hanya membutuhkan sedikit class pelaksana (implementasi).

Clases yang berpartisipasi dalam pola desain command adalah: Command : menyatakan sebuah interface untuk executing operasi;

Concrete Command : memperluas interface Command, menerapkan metode Execute dengan menerapkan operasi yang sesuai pada Receiver. Ini mendefinisikan hubungan antara Receiver dan action (tindakan).

Client (klien) penerima tersebut; Invoker tersebut; Receiver

: membuat objek ConcreteCommand dan menetapkan

: meminta command untuk melaksanakan/membawa permintaan

: mengetahui bagaimana untuk melakukan operasi,

Klien meminta command yang akan dieksekusi. Invoker mengambil/membawa command, merangkum dan menempatkannya dalam antrian (queue), dalam kasus tersebut ada hal lain yang harus dilakukan pertama kali, dan

ConcreteCommand yang bertanggung jawab atas command yang diminta, mengirim hasilnya ke Receiver.

Berikut ini adalah pola kode sampel implementasi klasik menempatkan pesanan untuk membeli dan menjual saham:

Klien menciptakan beberapa pesanan untuk membeli dan menjual saham (ConcreteCommands). Kemudian pesanan akan dikirim ke agen (Invoker). Agen mengambil pesanan dan menempatkan mereka ke sistem StockTrade (Receiver). Agen menyimpan antrian internal dengan pesanan untuk ditempatkan. Mari kita menganggap bahwa sistem StockTrade ditutup setiap hari Senin, namun agen tetap menerima pesanan, dan antrian untuk diproses di kemudian hari. public interface Order { public abstract void execute ( ); } // Receiver class. class StockTrade { public void buy() { System.out.println("You want to buy stocks"); } public void sell() { System.out.println("You want to sell stocks "); } } // Invoker. class Agent { private m_ordersQueue = new ArrayList();

public Agent() { } void placeOrder(Order order) { ordersQueue.addLast(order); order.execute(ordersQueue.getFirstAndRemove()); } } //ConcreteCommand Class. class BuyStockOrder implements Order { private StockTrade stock; public BuyStockOrder ( StockTrade st) { stock = st; } public void execute( ) { stock . buy( ); } } //ConcreteCommand Class. class SellStockOrder implements Order { private StockTrade stock; public SellStockOrder ( StockTrade st) { stock = st; } public void execute( ) { stock . sell( ); } } // Client public class Client { public static void main(String[] args) { StockTrade stock = new StockTrade(); BuyStockOrder bsc = new BuyStockOrder (stock); SellStockOrder ssc = new SellStockOrder (stock); Agent agent = new Agent(); agent.placeOrder(bsc); // Buy Shares agent.placeOrder(ssc); // Sell Shares } } Penerapan & Contoh

Penerapan pola desain Command dapat ditemukan dalam kasus ini di bawah ini: objek parameterizes tergantung pada tindakan mereka harus dilakukan.

menentukan atau menambahkan dalam antrian dan mengeksekusi permintaan pada waktu yang berbeda.

menawarkan dukungan untuk tindakan undoable (metode Execute dapat menghafal state dan memungkinkan akan kembali ke state tersebut).

struktur sistem dalam operasi tingkat tinggi yang didasarkan pada operasi primitif.

objek decouples yang memanggil tindakan dari objek yang melakukan tindakan. Penggunaan ini juga dikenal sebagai Produser pola desain konsumen. Contoh dari pemesanan makan di restoran adalah salah satu yang sangat baik

ketika mencoba untuk penjelasan yang lebih baik tentang bagaimana pattern ini bekerja: Pelayan (Invoker) mencatat pesanan subscribers di bukunya. Perintah ini kemudian diantrikan untuk pesanan yang akan dimasak dan akan dimasak (Receiver) di mana pesanan (makanan) itu diproses.

Dalam hal ini pelaku dalam skenario adalah sebagai berikut: Client adalah customer. Dia mengirimkan request kepada receiver (penerima) melalui waiter (pelayan), yang mana adalah Invoker. Waiter merangkum (encapsulates) command/perintah (order dalam kasus/hal ini) dengan menuliskan di cek dan kemudian menempatkannya, menciptakan obyek ConcreteCommand yang merupakan command itu sendiri. Receiver ini akan memasak pesanan tersebut,

setelah menyelesaikan semua order yang dikirim kepadanya sebelum command tersebut, mulai bekerja pada pesanan tersebut. Aspek lain yang terlihat dari contoh adalah kenyataan bahwa pad untuk pesanan tidak hanya mendukung pesanan dari menu, sehingga dapat mendukung command untuk memasak banyak item (makanan) yang berbeda. Dalam hal yang sama kita dapat mempertimbangkannya dari contoh sebuah toko auto-repair. Orang-orang datang dengan mobil yang berbeda yang memiliki masalah yang berbeda. Orang yang berada di meja depan mencatat informasi (masalah) para customer dan menempatkan mobil pada antrian untuk diperbaiki. Informasi (klaim) order yang dituliskan (encapsulated) dalam kertas yang akan digunakan pemilik mobil ketika ia datang kembali untuk mengambil mobilnya yang telah diperbaiki. Pada poin tertentu mobil akan menjadi item/hal pertama dalam antrian dan mekanik akan memperbaikinya. Sama seperti pada contoh di atas, Client adalah customer. Invoker adalah person yang berada di front desk yang mencatat informasi tentang mobil dan permasalahannya,

ConcreteCommand adalah request (permintaan) untuk memperbaiki mobil dan Receiver adalah mechanic.

Implementasi yang paling banyak digunakan dari command pattern adalah the one used to implement the undo options in applications:

Mari kita pertimbangkan calculator application. App lication

ini

merupakan

Client. Calculator (Receiver) kelas adalah kelas utama yang digunakan dalam aplikasi untuk melakukan command/perintah. Mungkin juga sebagai kelas dokumen jika kita memiliki aplikasi editor teks dan kita ngin menerapkan operasi seperti copy / paste / etc .... Ketika aplikasi harus melakukan command, itu menciptakan command dan mengirimkannya ke Invoker. Invoker memanggil execute metode command dan menambahkannya ke daftar yang berisi semua command/perintah. Metode execute dari command, mendelegasikan panggilan ke objek Calculator. Ketika undo operation yang dilakukan, Invoker menggunakan daftar dengan semua command yang pernah dijalankan dan panggilan untuk masing-masing unexecuted method. Operasi Redo bekerja dengan cara yang sama.

Spesifik masalah dan implementasi Sekarang kita telah memahami bagaimana pattern command bekerja, saatnya untuk melihat kelebihan dan kekurangannya, juga. Kecerdasan command/perintah

Ada dua ekstrem yang programmer harus dihindari saat menggunakan pattern ini: 1. Command ini hanya hubungan antara receiver dan action yang melaksanakan request 2. Command mengimplementasikan segala sesuatu sendiri, tanpa mengirim apapun ke receiver (penerima). Kita harus selalu ingat fakta bahwa receiver (penerima ) adalah orang yang tahu bagaimana untuk melakukan operasi yang dibutuhkan, tujuan dari

command/perintah adalah untuk membantu client dalam mendelegasikan request (permintaannya) secara cepat dan untuk memastikan command/perintah berakhir di mana seharusnya.

Undo dan Redo tindakan Sebagaimana disebutkan di atas, beberapa implementasi dari pola desain Command termasuk bagian untuk mendukung dalam undo dan redo dari tindakan. Dengan kata lain, dalam melakukan mekanisme untuk memperoleh states lama dari objek Receiver itu sangat diperlukan, dengan kata lain untuk mencapai hal ini ada dua pilihan: Sebelum menjalankan setiap command, snapshot dari state penerima (receiver) disimpan dalam memori. Hal ini tidak memerlukan banyak upaya pemrograman tapi tidak dapat selalu diterapkan. Misalnya melakukan hal ini dalam aplikasi pengolahan gambar akan memerlukan gambar yang tersimpan dalam memori setelah setiap langkah, yang secara praktis tidak mungkin. dalam menyimpan receiver objects states, himpunan operasi yang dilakukan disimpan dalam memori. Dalam hal ini command dan receiver/penerima classes harus menerapkan algoritma invers untuk membatalkan setiap tindakan. Ini akan membutuhkan usaha pemrograman tambahan, tetapi

sedikit memori yang akan diperlukan. Terkadang untuk tindakan undo / redo, command harus menyimpan informasi lebih lanjut tentang state(state) receiver objects. Ide yang bagus dalam kasus tersebut adalah dengan menggunakan Memento Pattern.

Asynchronous Metode Invocation

Penggunaan lain untuk pola desain command adalah untuk menjalankan command asynchronous di latar belakang sebuah aplikasi. Dalam hal ini Invoker berjalan di thread utama dan mengirimkan permintaan ke receiver yang berada (running) di thread yang terpisah. Invoker akan menyimpan antrian command yang akan dijalankan dan akan mengirim antrian command ke receiver sementara itu Asynchronous Metode Invocation menyelesaikan antrian command tersebut. Menggunakan satu thread yaitu di mana receiver berjalan/bekerja lebih dari thread yang dapat diciptakan untuk ini. Tapi untuk masalah kinerja (pembuatan thread adalah kebutuhan) jumlah thread harus dibatasi. Dalam hal ini Invoker akan menggunakan kolam thread penerima(receiver) untuk menjalankan command asynchronously.

Menambahkan command baru

Obyek command decouples adalah obyek yang memanggil(invokes) tindakan dari objek yang melakukan tindakan. Ada implementasi dari pola di mana Invoker instantiates adalah object command konkret. Dalam hal ini jika kita perlu menambahkan jenis perintah baru kita perlu mengubah Invoker juga. Dan ini akan melanggar Open Close Principle (OCP). Dengan kata lain, untuk memiliki kemampuan menambahkan command baru dengan usaha minimum, kita harus memastikan bahwa Invoker sadar hanya tentang command abstrak class atau interface.

Menggunakan perintah komposit

Ketika menambahkan command(perintah) baru dalam aplikasi, kita dapat menggunakan pola komposit untuk grup command yang ada di command baru lainnya. Dengan cara ini, macro dapat dibuat dari command yang ada.

Hot spot

The main advantage of the command design pattern is that it decouples the object that invokes the operation from the one that know how to perform it. And this advantage must be kept. There are implementations of this design pattern in which the invoker is aware of the concrete commands classes. This is wrong making the implementation more tightly coupled. The invoker should be aware only about the abstract command class. 3.3. Mediator Pattern Motivasi Dalam rangka untuk memiliki desain berorientasi objek yang baik kita harus membuat banyak kelas berinteraksi satu sama lain. Jika prinsip-prinsip tertentu tidak diterapkan maka framework final akan berakhir dengan berantakan total di mana setiap objek bergantung pada object-object lain dalam rangka untuk dijalankan. Untuk menghindari tight coupled frameworks, kita perlu sebuah mekanisme untuk memfasilitasi interaksi antara objek dalam cara bahwa objectobject yang tidak menyadari keberadaan object-object lainnya.

Mari kita ambil contoh dari layar. Ketika kita menciptakannya kita tambahkan semua jenis kontrol ke layar. Kontrol ini perlu berinteraksi dengan semua kontrol lainnya. Misalnya ketika tombol ditekan harus tahu apakah data tersebut valid dalam kontrol lainnya. Seperti yang Anda lihat jika Anda membuat aplikasi yang berbeda menggunakan form Anda tidak perlu memodifikasi setiap kelas kontrol setiap kali Anda menambahkan kontrol baru ke form. Semua operasi antara kontrol dikelola oleh bentuk kelas itu sendiri. Kelas ini disebut mediator.

Maksud

Mendefinisikan objek yang mengenkapsulasi bagaimana satu set objek berinteraksi. Mediator mempromosikan kopling longgar dengan menjaga objectobject dari mengacu satu sama lain secara eksplisit, dan itu memungkinkan Anda merubah interaksi mereka secara mandiri.

Implementasi Peserta Kelas-kelas peserta dalam pola ini adalah: Mediator - mendefinisikan sebuah interface untuk berkomunikasi dengan Colleague objects. ConcreteMediator - mengetahui colleague classes dan menyimpan referensi ke colleague objects. - Mengimplementasikan komunikasi dan mentransfer pesan antara colleague classes colleague classes - menyimpan referensi ke objek Mediator - Berkomunikasi dengan Mediator kapan itu akan dikomunikasikan dengan Colleague lain.

Penerapan

Menurut Gamma et al, pola Mediator harus digunakan bila: satu set objek berkomunikasi dengan cara yang didefinisikan dengan baik tapi kompleks. Menghasilkan ketergantungan yang tidak terstruktur dan sulit dipahami. sulit menggunakan kembali obyek karena mengacu pada dan berkomunikasi dengan banyak object lain. perilaku yang didistribusikan antara beberapa kelas harus disesuaikan tanpa banyak subclassing. Contoh Contoh 1 - Perpustakaan GUI Contoh mediator adalah salah satu pola yang sudah digunakan dalam berbagai aplikasi. Salah satu contoh yang diwakili oleh kelas Dialog dalam framework aplikasi GUI. Sebuah Dialog window adalah kumpulan kontrol grafis dan nongrafis. Kelas Dialog menyediakan mekanisme untuk memfasilitasi interaksi antara kontrol. Sebagai contoh, ketika sebuah nilai baru dipilih dari objek ComboBox Label memiliki untuk menampilkan nilai baru. Baik ComboBox dan label tidak menyadari setiap struktur lainnya dan semua interaksi dikelola oleh objek Dialog. Setiap kontrol tidak menyadari keberadaan kontrol lainnya.

Contoh 2 - Chat aplikasi Aplikasi chat adalah contoh lain dari pola mediator. Dalam aplikasi chatting kita dapat memiliki beberapa peserta. Ini bukan ide yang baik untuk menghubungkan setiap peserta untuk semua yang lain karena jumlah koneksi akan sangat tinggi, akan ada masalah teknis karena proxy dan firewall, dll .. . Solusi yang paling tepat adalah memiliki hub di mana semua peserta akan terhubung, hub ini hanya kelas mediator.

Peserta: Chatroom (Mediator) - Mendefinisikan interface untuk berinteraksi dengan peserta ChatroomImpl (ConcreteMediator) - mengimplementasikan operasi yang didefinisikan oleh interface Chatroom. Operasi yang mengelola interaksi antara object: ketika salah satu peserta mengirimkan pesan, pesan dikirim ke peserta lainnya. Peserta (collegue) - mendefinisikan sebuah interface bagi peserta. HumanParticipant, Bot (ConcreteCollegue) - mengimplementasikan peserta, peserta dapat menjadi manusia atau bot, masing-masing memiliki implementasi yang berbeda tetapi menerapkan interface yang sama. Setiap peserta akan tetap hanya sebuah referensi ke mediator.

Spesifik masalah dan implementasi Abstrak Mediator Tidak perlu untuk membuat kelas Mediator Abstrak atau interface asalkan colleagues akan menggunakan hanya satu mediator. Definisi dari Mediator abstrak diperlukan hanya jika colleagues perlu bekerja sama dengan mediator yang berbeda. Komunikasi antara mediator dan rekan Ada berbagai cara untuk mewujudkan komunikasi antara colleagues dan mediator nya: Salah satu metode yang paling sering digunakan adalah dengan menggunakan pola Observer. Mediator dapat juga meenjadi observer dan Kolega yang dapat menerapkan observable(diamati) object. Setiap kali perubahan yang dibuat dalam state objek observable (diamati), observer (mediator) akan diberitahu dan memberitahu semua colleagues (rekan) object lainnya. Metode alternatif dapat digunakan untuk mengirim pesan ke mediator. Misalnya sebuah delegasi sederhana dapat digunakan dan metode khusus dapat terekspos oleh mediator

Dalam implementasi lebih kompleks pesan asynchronous dapat ditambahkan ke ke antrian pesan, dari mana mereka dapat dijemput oleh objek mediator

Kompleksitas objek Mediator Obyek mediator menangani semua interaksi antara obyek peserta. Salah satu potensi masalah adalah kompleksitas mediator ketika jumlah peserta tinggi dan perbedaan kelas peserta yang tinggi. Jika Anda membuat dialog kustom untuk aplikasi GUI Anda ingat bahwa setelah beberapa waktu dialog kelas menjadi sangat kompleks karena mereka harus mengelola banyak operasi. Konsekuensi Seperti dengan pola desain yang lainnya, ada keuntungan dan kerugian dari menggunakan Pola Mediator. Bagian berikut akan menguraikan secara singkat beberapa isu-isu ini. Keuntungan Pemahaman - Mediator merangkum logika dari mediasi antara rekan-rekan. Dari alasan ini 'lebih mudah untuk memahami logika ini karena disimpan hanya dalam satu kelas. Decoupled Colleagues - Kelas colleague yang benar-benar dipisahkan. Menambahkan kelas colleague baru sangat mudah karena tingkat decoupling. objek protokol Sederhana - Obyek colleague perlu berkomunikasi hanya dengan object mediator. Praktis pola mediator mengurangi saluran komunikasi yang dibutuhkan (protokol) dari banyak ke banyak ke satu ke banyak dan banyak ke satu (many to many to one to many and many to one). Batas Subclassing - Karena logika komunikasi seluruh dirumuskan oleh kelas mediator, ketika logika ini perlu diperpanjang hanya kelas mediator perlu diperpanjang. Kekurangan Kompleksitas - dalam prakteknya mediator cenderung menjadi lebih kompleks dan rumit. Sebuah praktek yang baik adalah untuk berhati-hati untuk membuat kelas mediator hanya bertanggung jawab untuk bagian komunikasi. Misalnya saat mengimplementasikan layar yang berbeda kelas layar seharusnya tidak

berisi kode yang bukan merupakan bagian dari operasi layar. Ini harus dimasukkan ke dalam beberapa kelas lain. Pattern yang Terkait Ada beberapa pola desain yang sangat erat kaitannya dengan pola Mediator dan sering digunakan dalam hubungannya dengan itu. Facade Pattern - mediator disederhanakan menjadi pola facade jika mediator adalah hanya kelas aktif dan colleagues adalah kelas pasif. Pola facade hanyalah sebuah implementasi dari pola mediator yang mana mediator adalah satu-satunya obyek yang memicu(triggering) dan menyerukan (invoking) tindakan di kelas colleagues pasif. Facade ini menjadi panggilan oleh beberapa kelas eksternal. Adapter Pattern - pattern mediator hanya "menengahi" permintaan antara kelas colleague. Hal ini tidak seharusnya mengubah pesan yang diterima dan dikirimnya, jika itu mengubah pesan tersebut maka itu adalah sebuah Adapter pattern. Observer Pattern observer dan mediator adalah pola yang

sama/mirip/similiar, memecahkan masalah yang sama. Perbedaan utama antara mereka adalah masalah alamat mereka. Observer pattern menangani komunikasi antara observer dan subjek atau beberapa subjek. Ini sangat mungkin untuk memiliki object-object yang dapat diamati baru ditambahkan. Di sisi lain dalam pola mediator, kelas mediator adalah kelas paling mungkin

diwariskan(inherited). Known Uses Pada bagian berikut, kita akan membahas beberapa penggunaan pola Mediator di dunia nyata. Anda akan menemukan Mediator dalam banyak situasi di mana terdapat banyak komponen yang harus berinteraksi dengan satu sama lain dalam cara yang kompleks. User Interfaces Mungkin pola mediator banyak digunakan dalam user interface. Hampir setiap framework kerja GUI dibangun di sekitarnya. Seperti dibahas sebelumnya, kelas yang mewakili forms (Dialog, Form, ...) merupakan mediator dan kontrol masing-masing mewakili seorang rekan. Kelas form menyediakan mekanisme

untuk memfasilitasi interaksi antara kontrol, sebuah inherited class dibuat setiap kali layar baru dibuat dan kode yang spesifik ditulis di kelas ini. Dengan cara ini, komunikasi kontrol dimediasi oleh kelas form ini. Java Message Service The Java Message Service (JMS) API adalah Java Message Oriented Middleware (MOM) API untuk mengirimkan pesan antara dua atau lebih klien. JMS API mendukung 2 model. Salah satunya adalah model publikasiberlangganan (publish-subscribe). Ini merupakan implementasi dari pola mediator. Pesan dapat dipublish berdasarkan topik tertentu. Penerbit harus membuat langganan (subscription) untuk subscribers yang berbeda dapat berlangganan. Nol atau lebih subscribers dapat berlangganan untuk menerima pesan pada topik pesan tertentu. Penerbit dan subscribers tidak tahu satu sama lain, subscribers dapat tidak aktive. Dalam hal ini subscribers menerima pesan saat kembali aktif. Kesimpulan Mediator pattern digunakan untuk mengambil peran sebuah hub atau router dan memfasilitasi komunikasi antara banyak kelas. Sebuah cara yang sama dapat dibuat dengan sistem database. The mediator transform a hard to implement relation of many to many, where each calls has to communicate with each other class, in 2 relations, easy to implement, of many to one and one to many, where the communication is handled by the mediator class. 3.4. Memento Pattern Motivasi Hal ini kadang-kadang diperlukan untuk menangkap state internal suatu obyek di beberapa titik dan memiliki kemampuan untuk mengembalikan objek ke state yang kemudian pada waktunya. Kasus seperti ini berguna jika terjadi kesalahan atau kegagalan. Pertimbangkan kasus objek kalkulator dengan undo operation seperti kalkulator hanya bisa mempertahankan daftar semua operasi sebelumnya yang telah dilakukan dan dengan demikian akan mampu mengembalikan perhitungan sebelumnya yang telah dilakukan. Hal ini akan menyebabkan objek kalkulator untuk menjadi lebih besar, lebih kompleks, dan heavyweight, sebagai objek kalkulator yang harus menyediakan tambahan fungsi undo dan harus

menjaga daftar semua operasi sebelumnya. Fungsi ini bisa pindah dari kelas kalkulator, sehingga menjadi sebuah eksternal (undo manager class) dapat mengumpulkan state internal kalkulator dan menyimpannya. Namun

menyediakan akses eksplisit untuk setiap variabel state kalkulator untuk manajer restore akan menjadi tidak praktis dan akan melanggar prinsip enkapsulasi. Maksud Tujuan dari pola ini adalah untuk menangkap state internal obyek tanpa melanggar enkapsulasi dan dengan demikian memberikan rata-rata untuk merestore objek ke state awal bila diperlukan. Implementasi Gambar di bawah menunjukkan diagram UML kelas untuk Pola Memento:

Memento Menyimpan state internal objek Originator. State dapat mencakup sejumlah variabel state. The Memento harus memiliki dua interface, satu interface kepada CareTaker. Interface ini tidak boleh mengizinkan operasi atau akses ke internal state yang sudah disimpan oleh memento dan kemurnian enkapsulasi (thus honors encapsulation). Interface lainnya adalah originator(pencetus/pemula) dan memungkinkan originator(pencetus/pemula) untuk mengakses setiap variabel state yang perlu untuk originator(pencetus/pemula) untuk mengembalikan (restore) state (state) sebelumnya. Originator Membuat objek memento untuk menangkap originators internal state. Gunakan obyek memento untuk mengembalikan state/ state sebelumnya.

Caretaker Bertanggung jawab untuk menjaga memento. Memento adalah buram/opaque kepada Caretaker, dan Caretaker tidak boleh beroperasi di atasnya.

Sebuah Caretaker ingin melakukan operasi pada Originator sementara memiliki kemungkinan untuk rollback. Caretaker memanggil metode createMemento() pada originator, meminta originator untuk menambahkan sebuah objek memento. Pada titik ini originator menciptakan objek memento menyimpan state (state) internal dan passing memento kepada carataker. Caretaker maintain obyek memento dan melakukan operasi. Dalam hal kebutuhan untuk operasi undo, Caretaker memanggil metode setMemento() pada originator dengan passing maintained memento object. Originator akan menerima memento,

menggunakannya untuk merestore ke previous state.

Penerapan & Contoh

Pola memento digunakan ketika snapshot state obyek harus ditangkap sehingga dapat dikembalikan ke state yang kemudian dan dalam situasi di mana secara eksplisit parsing state objek akan melanggar enkapsulasi.

Contoh - Kalkulator sederhana dengan Undo Operasi.

contoh sederhana adalah kalkulator yang menemukan hasil penambahan dua angka, dengan opsi tambahan untuk membatalkan operasi terakhir dan mengembalikan hasil sebelumnya.

The code below shows the memento object interface to caretaker. Note that this interface is a placeholder only and has no methods to honor encapsulation in that the memento is opaque to the caretaker.

package memento;

/** * Memento interface to CalculatorOperator (Caretaker) */ public interface PreviousCalculationToCareTaker { // no operations permitted for the caretaker } The code below shows the Memento to Originator interface; note that this interface provides the necessary methods for the originator to restore its original state.

package memento;

/** * Memento Interface to Originator * * This interface allows the originator to restore its state */ public interface PreviousCalculationToOriginator {

public int getFirstNumber(); public int getSecondNumber(); } The code below shows the memento implementation, note that the memento must implement two interfaces, the one to the caretaker as well as the one to the originator.

package memento;

/** * Memento Object Implementation * * Note that this object implements both interfaces to Originator and CareTaker

*/ public class PreviousCalculationImp implements

PreviousCalculationToCareTaker, PreviousCalculationToOriginator {

private int firstNumber; private int secondNumber;

public PreviousCalculationImp(int firstNumber, int secondNumber) {

this.firstNumber = firstNumber; this.secondNumber = secondNumber; }

@Override public int getFirstNumber() {

return firstNumber; }

@Override public int getSecondNumber() {

return secondNumber; } } The code below shows the calculator interface which is the originator interface

package memento;

/** * Originator Interface */ public interface Calculator {

// Create Memento public PreviousCalculationToCareTaker backupLastCalculation();

// setMemento public memento); void restorePreviousCalculation(PreviousCalculationToCareTaker

// Actual Services Provided by the originator public int getCalculationResult(); public void setFirstNumber(int firstNumber); public void setSecondNumber(int secondNumber); } The code below shows the Calculator implementation which is the originator implementation. Note that the backupLastCalculation method corresponds to createMemento() method discussed previously, in this method the memento object is created and all originator state is saved to the memento. Also note that the method restorePreviousCalculation() method corresponds to setMemento() method . Inside this method the logic to restore the previous state is executed.

package memento;

/** * Originator Implementation */ public class CalculatorImp implements Calculator {

private int firstNumber; private int secondNumber;

@Override public PreviousCalculationToCareTaker backupLastCalculation() {

// create a memento object used for restoring two numbers

return new PreviousCalculationImp(firstNumber,secondNumber); }

@Override public int getCalculationResult() {

// result is adding two numbers return firstNumber + secondNumber; }

@Override public void restorePreviousCalculation(PreviousCalculationToCareTaker

memento) {

this.firstNumber ((PreviousCalculationToOriginator)memento).getFirstNumber(); this.secondNumber ((PreviousCalculationToOriginator)memento).getSecondNumber(); }

@Override public void setFirstNumber(int firstNumber) {

this.firstNumber = firstNumber; }

@Override public void setSecondNumber(int secondNumber) {

this.secondNumber = secondNumber; } } The code below shows the calculator driver which simulates a user using the calculator to add numbers, the user calculates a result, then enters wrong

numbers, he is not satisfied with the result and he hits Ctrl + Z to undo last operation and restore previous result.

package memento;

/** * CareTaker object */ public class CalculatorDriver {

public static void main(String[] args) {

// program starts Calculator calculator = new CalculatorImp();

// assume user enters two numbers calculator.setFirstNumber(10); calculator.setSecondNumber(100);

// find result System.out.println(calculator.getCalculationResult());

// Store result of this calculation in case of error PreviousCalculationToCareTaker calculator.backupLastCalculation(); memento =

// user enters a number calculator.setFirstNumber(17);

// user enters a wrong second number and calculates result calculator.setSecondNumber(-290);

// calculate result System.out.println(calculator.getCalculationResult());

// user hits CTRL + Z to undo last operation and see last result calculator.restorePreviousCalculation(memento);

// result restored System.out.println(calculator.getCalculationResult()); } }

Spesifik masalah dan implementasi Basis Data Transaksi operasi Transaksi pada database yang terjadi dalam atomic, consistent, durable, and isolated fashion. Sebuah transaksi dapat berisi beberapa operasi pada database, setiap operasi dapat berhasil atau gagal, namun transaksi menjamin bahwa jika semua operasi berhasil, transaksi akan dilakukan (commit) dan akan bersifat final. Dan jika operasi gagal, maka transaksi akan gagal dan semua operasi akan dirollback dan meninggalkan database seolah-olah tidak ada yang terjadi. mekanisme rollback menggunakan pola desain memento. Pertimbangkan obyek yang mewakili tabel database, manajer transaksi objek yang bertanggung jawab melakukan transaksi harus melakukan operasi pada objek meja sementara memiliki kemampuan untuk membatalkan operasi jika gagal, atau jika ada operasi pada setiap objek meja lainnya gagal. Untuk dapat rollback, obyek manajer transaksi akan meminta objek table untuk memento sebelum melakukan operasi dan dengan demikian dalam kasus kegagalan, obyek memento akan digunakan untuk mengembalikan tabel ke state sebelumnya. Konsekuensi Memento melindungi enkapsulasi dan menghindari mengekspos state internal dan implementasi originator. Hal ini juga menyederhanakan kode originator sehingga originator tidak perlu melacak state sebelumnya karena ini adalah tanggung jawab CareTaker.

Menggunakan pola memento dapat menjadi mahal tergantung pada jumlah informasi state yang harus disimpan di dalam obyek memento. Selain itu caretaker harus berisi logika tambahan untuk dapat mengelola memento.

Pattern Terkait Pola Command Command dapat menggunakan memento untuk

mempertahankan state untuk operasi undoable.

Known Uses Undo dan restore operasi di sebagian besar perangkat lunak. Transaksi database yang sudah dibahas sebelumnya. 3.5. Observer Pattern Motivasi Kita tidak bisa bicara tentang Pemrograman Berorientasi Obyek tanpa mempertimbangkan state (state) objek. Semua pemrograman berorientasi objek adalah tentang objek dan interaksinya. Kasus-kasus ketika objek-objek tertentu perlu diberitahu tentang perubahan yang terjadi di objek lain itu sering terjadi. Untuk memiliki desain yang baik berarti untuk memisahkan (decouple) sebanyak mungkin dan untuk mengurangi dependensi. Pola Desain Observer dapat digunakan setiap kali sebuah subjek harus diamati oleh satu atau lebih observer. Mari kita asumsikan kita memiliki sebuah sistem saham yang menyediakan data untuk beberapa jenis klien. Kami ingin punya klien diimplementasikan sebagai aplikasi berbasis web tetapi dalam waktu dekat kita perlu menambahkan klien untuk perangkat mobile, Palm atau Pocket PC, atau untuk memiliki sistem untuk memberitahukan pengguna dengan peringatan sms. Sekarang mudah untuk melihat apa yang kita butuhkan dari pola observer (observer): kita perlu untuk memisahkan subjek (stock(saham) server) dari observer (observer) itu (aplikasi client) sedemikian rupa bahwa menambahkan observer (observer) baru akan menjadi transparan untuk server.

Maksud

Mendefinisikan ketergantungan satu-ke-banyak antara obyek sehingga ketika satu objek berubah state/stateya, semua dependen (yang tergantung/ tanggungan) telah diberitahu dan diupdate secara otomatis.

Implementasi Kelas-kelas peserta dalam pola ini adalah: Observable - interface atau kelas abstrak mendefinisikan operasi untuk attaching dan de-attaching oberserver kepada klien. Dalam buku GOF kelas / interface ini dikenal sebagai Subyek. ConcreteObservable - concrete Observable class. It maintain the state of the object and when a change in the state occurs it notifies the attached Observers. Observer - interface atau kelas abstrak mendefinisikan operasi yang akan digunakan untuk memberitahu objek ini. ConcreteObserverA, implementations. Aliran ini sederhana: framework utama instantiate obyek ConcreteObservable. Kemudian instantiate dan menempel concrete observers untuk menggunakan metode yang didefinisikan dalam Observable interface. Setiap kali perubahan state subjek itu memberitahu semua observer yang terpasang dengan ConcreteObserver2 concrete Observer

menggunakan metode yang didefinisikan dalam interface Observer. Ketika Observer baru ditambahkan ke aplikasi, semua yang perlu kita lakukan adalah untuk instantiate dalam framework utama dan menambahkan melampirkannya ke objek diamati. Kelas-kelas yang sudah dibuat akan tetap tidak berubah. Penerapan & Contoh Pola observer digunakan ketika: the change of a state in one object must be reflected in another object without keeping the objects tight coupled. the framework we are writing needs to be enhanced in future with new observers with minimal changes Beberapa Contoh Klasik: Model View Controller Pattern - The observer pattern is used in the model view controller (MVC) architectural pattern. In MVC the this pattern is used to decouple the model from the view. View represents the Observer and the model is the Observable object. Event management - This is one of the domains where the Observer patterns is extensively used. Swing and .Net are extensively using the Observer pattern for implementing the events mechanism.

Contoh - Kantor Berita Mari kita 'mengambil contoh dari kantor berita. Sebuah kantor berita mengumpulkan berita berita dan mempublikasikan mereka ke subscribers yang berbeda. Kita perlu membuat framework kerja untuk dan agency untuk dapat menginformasikan segera, ketika peristiwa terjadi, subscribersnya tentang acara tersebut. Para subscribers dapat menerima berita dengan cara yang berbeda: Email, SMS, ... Solusinya perlu luas cukup untuk mendukung jenis baru dari subscribers (mungkin teknologi komunikasi baru akan muncul).

Jelas, agency diwakili oleh kelas Observable(Subject) bernama NewsPublisher. Yang satu ini dibuat sebagai kelas abstrak karena agency ingin membuat beberapa jenis objek diamati: di awal hanya untuk berita bisnis, tapi setelah beberapa waktu olahraga dan politik baru akan diterbitkan. Kelas concretenya adalah BusinessNewsPublisher .

Logika observer diimplementasikan dalam NewsPublisher. Ini membuat list semua subscribers dan menginformasikan mereka tentang berita terbaru. Para subscribers yang diwakili oleh beberapa observer (SMSSubscriber,

EmailSubscriber). Kedua observer yang disebutkan di atas diwarisi dari Subscribers tersebut. Subscribers adalah kelas abstrak yang dikenal untuk penerbit. Penerbit tidak tahu tentang observer concrete, ia tahu hanya sekitar abstraksi mereka.

Di kelas utama sebuah penerbit(observable) dibangun dan beberapa subscribers (observer). Para subscribers yang berlangganan ke penerbit, mereka dapat berhenti berlangganan. Dalam arsitektur ini, jenis baru dari subscribers dapat dengan mudah ditambahkan (instant messaging, ...) dan jenis baru dari penerbit (Berita Cuaca, Olahraga Berita, ...).

Specific Implementation Problems

Banyak subyek Banyak observer

Ini bukan situasi umum tetapi ada kasus ketika ada banyak observer yang perlu mengamati lebih dari satu subjek. Dalam hal ini observer perlu diberitahu tidak hanya tentang perubahan, tetapi juga subjek yang merupakan subjek dengan state berubah. Hal ini dapat diwujudkan sangat sederhana dengan menambahkan referensi subjek dalam metode pemberitahuan pembaruan. Subjek akan melewati sebuah referensi untuk dirinya sendiri (ini) untuk ketika memberitahu observer.

Siapa yang memicu update? Komunikasi antara subjek dan observer yang dilakukan melalui metode notify yang dideklarasikan dalam interface observer. Tapi siapa yang dapat dipicu baik dari subjek atau objek observer. Biasanya metode notify dipicu oleh subjek ketika state itu berubah. Tapi kadang-kadang ketika update itu sering, perubahan berturut-turut dalam subjek akan menentukan banyak operasi refresh yang tidak perlu di observer. Dalam rangka untuk membuat proses ini lebih efisien observer dapat bertanggung jawab untuk memulai operasi notify ketika dianggap perlu.

Making sure Subject state is self-consistent before notification State subjek harus konsisten ketika operasi notify dipicu. Jika perubahan yang dibuat di state subjek setelah observer diberitahu, itu akan direfresh dengan state tua. Hal ini tampaknya sulit untuk dicapai tetapi dalam praktek ini dapat dengan mudah dilakukan ketika operasi subclass Subyek memanggil operasi yang diwariskan. Dalam contoh berikut, observer diberitahu ketika subjek berada dalam state tidak konsisten:

class Observable{ ... int state = 0; int additionalState = 0; public updateState(int increment)

{ state = state + increment; notifyObservers(); } ... }

class ConcreteObservable extends Observable{ ... public updateState(int increment){ super.updateState(increment); // the observers are notified additionalState = additionalState + increment; // the state is changed after the notifiers are updated } ... }

Perangkap ini dapat dihindari dengan menggunakan metode template di superclass subjek abstrak untuk memanggil operasi notify. Kemudian subclass subjek akan mengimplementasi operasi (s) dari template tersebut:

class Observable{ ... int state = 0; int additionalState = 0; public void final updateState(int increment) { doUpdateState(increment); notifyObservers(); } public void doUpdateState(int increment) { state = state + increment;

} ... }

class ConcreteObservable extends Observable{ ... public doUpdateState(int increment){ super.doUpdateState(increment); // the observers are notified additionalState = additionalState + increment; // the state is changed after the notifiers are updated } ... }

Operasi yang didefinisikan di kelas dasar subjek yang memicu operasi notify harus didokumentasikan.

Push dan pull metode komunikasi Ada 2 metode dari passing data dari subjek ke observer ketika state sedang berubah di sisi subjek: Puss Model - Subyek mengirim informasi rinci tentang perubahan observer apakah itu digunakan atau tidak. Karena subjek harus mengirim informasi rinci observer ini mungkin tidak efisien ketika sejumlah besar data perlu dikirim dan tidak digunakan. Aproach lain akan mengirim hanya informasi yang diperlukan oleh observer. Dalam hal ini subjek harus dapat membedakan antara berbagai jenis observer dan untuk mengetahui data yang dibutuhkan dari masing-masing, yang berarti bahwa lapisan subjek lebih digabungkan ke lapisan observer. Pull Model - Subjek hanya memberitahukan observer ketika perubahan di state itu muncul dan itu adalah tanggung jawab setiap observer untuk menarik data yang dibutuhkan dari subjek. Hal ini bisa menjadi tidak efisien karena komunikasi yang dilakukan dalam 2 langkah dan masalah yang mungkin muncul dalam lingkungan multithreading.

Specifying points of interests Efisiensi dapat ditingkatkan dengan menetapkan yang merupakan peristiwa yang setiap observer tertarik. Hal ini dapat diwujudkan dengan menambahkan kelas baru mendefinisikan suatu aspek. When an observer is registering it will provide the aspects in which it is interested:

class Subject{ ... void attach(Observer observer, Aspect interest); ... }

Encapsulating complex update semantics Ketika kita memiliki beberapa subjects dan observer, hubungan antara mereka akan menjadi lebih kompleks. Pertama, yang memiliki hubungan many-to-many lebih sulit untuk mengelola secara langsung. Kedua, hubungan antara subjek dan observer dapat berisi beberapa logika. Mungkin kita ingin memiliki observer yang diberitahu hanya ketika semua subjek akan mengubah state mereka. Dalam hal ini kita harus memperkenalkan objek lain yang bertanggung jawab (disebut ChangeManager) untuk actions berikut: untuk memaintain hubungan many to many antara subyek dan observer mereka. untuk encapsulate logika untuk memberitahukan observer. untuk menerima pemberitahuan dari subyek dan mendelegasikannya ke observer (based on the logic it encapsulate)

Pada dasarnya Change Manager adalah observer karena jika akan diberitahu tentang perubahan subjek dan dalam waktu yang sama adalah suatu subjek karena memberitahu observer. The ChangeManager adalah implemenasi dari pola Mediator.

Pola Observer biasanya digunakan dalam kombinasi dengan pola desain lainnya: Factory pattern - Ini sangat mungkin menggunakan factory pattern untuk menciptakan observer sehingga tidak ada perubahan yang diperlukan bahkan dalam framework utama. Observer baru dapat ditambahkan secara langsung dalam file konfigurasi. Template Method - Pola observer dapat digunakan bersama dengan Pola Metode Template untuk memastikan bahwa state Subjek konsisten sebelum pemberitahuan. Mediator Pattern - Pola mediator dapat digunakan ketika kita memiliki kasus-kasus kompleks yang mempunyai banyak subjek dan banyak observer. 3.6. IV.