"Halo, teman!"

"Hai, Bilaabo!"

"Kita masih punya waktu, jadi aku akan memberitahumu tentang tiga pola lagi."

"Tiga lagi? Berapa semuanya?"

"Saat ini ada lusinan pola populer, tetapi jumlah «solusi yang berhasil» tidak terbatas."

"Begitu. Jadi aku harus mempelajari beberapa lusin pola?"

"Sampai Anda memiliki pengalaman pemrograman yang nyata, mereka tidak akan memberi Anda banyak."

"Sebaiknya Anda mendapatkan lebih banyak pengalaman, dan kemudian, dalam setahun, kembali ke topik ini dan mencoba memahaminya lebih dalam. Setidaknya beberapa lusin pola desain paling populer."

"Adalah dosa untuk tidak menggunakan pengalaman orang lain dan malah menciptakan sesuatu untuk ke-110 kalinya."

"Saya setuju."

"Kalau begitu mari kita mulai."

Pola adaptor (atau pembungkus).

Pola: Adaptor, Proksi, Jembatan - 1

"Bayangkan Anda datang ke China dan menemukan bahwa outlet listrik mengikuti standar yang berbeda. Lubangnya tidak bulat, tetapi rata. Dalam hal ini, Anda memerlukan adaptor."

"Hal serupa juga bisa terjadi dalam pemrograman. Kelas beroperasi pada antarmuka yang serupa tetapi berbeda. Jadi kita perlu membuat adaptor di antara keduanya."

"Beginilah tampilannya:"

Contoh
interface Time
{
 int getSeconds();
 int getMinutes();
 int getHours();
}

interface TotalTime
{
 int getTotalSeconds();
}

"Misalkan kita memiliki dua antarmuka: Time  dan  TotalTime ."

"Antarmuka Waktu memungkinkan Anda mendapatkan waktu saat ini menggunakan metode getSeconds (),  getMinutes () dan  getHours ()."

" Antarmuka TotalTime memungkinkan Anda mendapatkan jumlah detik yang telah berlalu dari tengah malam hingga saat ini."

"Apa yang harus kita lakukan jika kita memiliki objek TotalTime , tetapi kita memerlukan objek Waktu atau sebaliknya?"

"Kita bisa menulis kelas adaptor untuk ini. Misalnya:"

Contoh
 class TotalTimeAdapter implements Time
{
 private TotalTime totalTime;
 public TotalTimeAdapter(TotalTime totalTime)
 {
  this.totalTime = totalTime;
 }

 int getSeconds()
 {
  return totalTime.getTotalSeconds() % 60; // seconds
 }

 int getMinutes()
 {
  return totalTime.getTotalSeconds() / 60; // minutes
 }

 int getHours()
 {
  return totalTime.getTotalSeconds() / (60 * 60); // hours
 }
}
 
Penggunaan
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());

"Dan adaptor ke arah lain:"

Contoh
class TimeAdapter implements TotalTime
{
 private Time time;
 public TimeAdapter(Time time)
 {
  this.time = time;
 }

 int getTotalSeconds()
 {
  return time.getHours() * 60 * 60 + time.getMinutes() * 60 + time.getSeconds();
 }
}
Penggunaan
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());

"Ah. Aku menyukainya. Tapi apakah ada contohnya?"

"Tentu saja! Sebagai contoh, InputStreamReader adalah adaptor klasik. Ini mengubah InputStream menjadi Pembaca."

"Terkadang pola ini juga disebut pembungkus, karena kelas baru 'membungkus' objek lain."

"Anda dapat membaca beberapa hal menarik lainnya di sini ."

Pola proxy

"Pola proxy agak mirip dengan pola pembungkus. Tetapi tujuannya bukan untuk mengubah antarmuka, tetapi untuk mengontrol akses ke objek asli yang disimpan di dalam kelas proxy. Selain itu, baik kelas asli maupun proxy biasanya memiliki antarmuka yang sama, yang membuatnya lebih mudah untuk mengganti objek dari kelas asli dengan objek proxy."

"Misalnya:"

Antarmuka kelas nyata
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
Implementasi kelas asli
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
Implementasi kelas proxy
class BankSecurityProxy implements Bank
{
 private Bank bank;
 public BankSecurityProxy(Bank bank)
 {
  this.bank = bank;
 }
 public void setUserMoney(User user, double money)
 {
  if (!SecurityManager.authorize(user, BankAccounts.Manager))
  throw new SecurityException("User can’t change money value");

  bank.setUserMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  if (!SecurityManager.authorize(user, BankAccounts.Manager))
  throw new SecurityException("User can’t get money value");

  return bank.getUserMoney(user);
 }
}

"Dalam contoh di atas, kami menjelaskan antarmuka Bank dan kelas CitiBank , implementasi dari antarmuka ini."

"Antarmuka memungkinkan Anda mendapatkan atau mengubah saldo akun pengguna."

Dan kemudian kami membuat BankSecurityProxy , yang juga mengimplementasikan antarmuka Bank dan menyimpan referensi ke antarmuka Bank yang berbeda. Metode kelas ini memeriksa apakah pengguna adalah pemilik akun atau manajer bank. Jika tidak, maka SecurityException dilempar."

"Begini cara kerjanya dalam praktik:"

Kode tanpa pemeriksaan keamanan:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
Kode dengan pemeriksaan keamanan:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

"Pada contoh pertama, kita membuat objek bank dan memanggil metode setUserMoney .

"Dalam contoh kedua, kami membungkus objek bank asli dalam objek BankSecurityProxy . Objek tersebut memiliki antarmuka yang sama, sehingga kode selanjutnya tetap berfungsi seperti semula. Namun sekarang pemeriksaan keamanan akan dilakukan setiap kali metode dipanggil."

"Dingin!"

"Ya. Anda dapat memiliki banyak proxy seperti itu. Misalnya, Anda dapat menambahkan proxy lain yang memeriksa apakah saldo akun terlalu besar. Manajer bank mungkin memutuskan untuk memasukkan banyak uang ke dalam akunnya sendiri dan melarikan diri ke Kuba dengan dana tersebut. ."

"Terlebih lagi... Pembuatan semua rantai objek ini dapat dimasukkan ke dalam kelas BankFactory , di mana Anda dapat mengaktifkan/menonaktifkan yang Anda butuhkan."

" BufferedReader bekerja menggunakan prinsip yang sama. Ini adalah Reader , tetapi berfungsi tambahan."

"Pendekatan ini memungkinkan Anda «merakit» objek dengan fungsionalitas yang diperlukan dari berbagai «bagian»."

"Oh, saya hampir lupa. Proxy digunakan jauh lebih luas daripada yang baru saja saya tunjukkan. Anda dapat membaca tentang kegunaan lain di sini ."

Pola jembatan

Pola: Adaptor, Proksi, Jembatan - 2

"Kadang-kadang saat sebuah program berjalan, fungsionalitas objek perlu diubah secara signifikan. Misalnya, Anda memiliki permainan dengan karakter keledai yang kemudian diubah menjadi naga oleh penyihir. Naga memiliki perilaku dan sifat yang sama sekali berbeda, tetapi itu adalah objek yang sama!"

"Tidak bisakah kita membuat objek baru dan menyelesaikannya?"

"Tidak selalu. Misalkan keledaimu berteman dengan sekelompok karakter, atau mungkin berada di bawah pengaruh beberapa mantra, atau terlibat dalam quest tertentu. Dengan kata lain, objek tersebut mungkin sudah digunakan di banyak tempat— dan ditautkan ke banyak objek lain. Jadi dalam hal ini, bukanlah opsi untuk sekadar membuat objek baru."

"Nah, lalu apa yang bisa dilakukan?"

"Pola Jembatan adalah salah satu solusi paling sukses."

"Pola ini memerlukan pemisahan objek menjadi dua objek: «objek antarmuka» dan «objek implementasi»."

"Apa perbedaan antara antarmuka dan kelas yang mengimplementasikannya?"

"Dengan antarmuka dan kelas, kita berakhir dengan satu objek. Tapi di sini — kita punya dua. Lihat contoh ini:"

Contoh
class User
{
 private UserImpl realUser;

 public User(UserImpl impl)
 {
  realUser = impl;
 }

 public void run() //Run
 {
  realUser.run();
 }

 public void fly() //Fly
 {
  realUser.fly();
 }
}

class UserImpl
{
 public void run()
 {
 }

 public void fly()
 {
 }
}

"Dan kemudian Anda dapat mendeklarasikan beberapa subclass dari UserImpl, misalnya UserDonkey (keledai) dan UserDragon (naga)."

"Tetap saja, aku tidak begitu mengerti bagaimana ini akan berhasil."

"Yah, sesuatu seperti ini:"

Contoh
class User
{
 private UserImpl realUser;

 public User(UserImpl impl)
 {
  realUser = impl;
 }

 public void transformToDonkey()
 {
  realUser = new UserDonkeyImpl();
 }

 public void transformToDragon()
 {
  realUser = new UserDragonImpl();
 }
}
Bagaimana itu bekerja
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

"Jadi itu seperti proxy."

"Ya, tapi dalam proxy, objek utama dapat disimpan di suatu tempat secara terpisah, dan kodenya bekerja dengan proxy sebagai gantinya. Di sini kami mengatakan bahwa setiap orang bekerja dengan objek utama, tetapi bagian-bagiannya berubah secara internal."

"Ah. Terima kasih. Maukah Anda memberi saya tautan untuk membaca lebih lanjut tentang itu?"

"Tentu saja, Amigo, temanku. Ini dia: Pola jembatan ."