"Hai kawan!"

"Hai, Bilaabo!"

"Kami masih mempunyai sedikit masa lagi, jadi saya akan memberitahu anda tentang tiga lagi corak."

"Tiga lagi? Berapa jumlah semuanya?"

"Pada masa ini terdapat berpuluh-puluh corak yang popular, tetapi bilangan «penyelesaian yang berjaya» adalah tidak terhad."

"Saya faham. Jadi saya perlu belajar beberapa dozen corak?"

"Sehingga anda mempunyai pengalaman pengaturcaraan sebenar, mereka tidak akan memberi anda banyak."

"Lebih baik anda mendapat lebih sedikit pengalaman, dan kemudian, dalam setahun, kembali kepada topik ini dan cuba memahaminya dengan lebih mendalam. Sekurang-kurangnya beberapa dozen corak reka bentuk yang paling popular."

"Adalah satu dosa untuk tidak menggunakan pengalaman orang lain dan sebaliknya mencipta sesuatu untuk kali ke-110."

"Saya setuju."

"Kalau begitu mari kita mulakan."

Corak penyesuai (atau pembalut).

Corak: Penyesuai, Proksi, Jambatan - 1

"Bayangkan anda datang ke China dan mendapati alur keluar elektrik mengikut piawaian yang berbeza. Lubangnya tidak bulat, tetapi rata. Dalam kes ini, anda memerlukan penyesuai."

"Sesuatu yang serupa juga boleh berlaku dalam pengaturcaraan. Kelas beroperasi pada antara muka yang serupa tetapi berbeza. Jadi kita perlu membuat penyesuai di antara mereka."

"Beginilah rupanya:"

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

interface TotalTime
{
 int getTotalSeconds();
}

"Andaikan kita mempunyai dua antara muka: Masa  dan  TotalTime ."

"Antara muka Masa membolehkan anda mendapatkan masa semasa menggunakan kaedah getSeconds (),  getMinutes () dan  getHours ()."

" Antara muka TotalTime membolehkan anda mendapatkan bilangan saat yang telah berlalu dari tengah malam ke saat semasa."

"Apakah yang perlu kita lakukan jika kita mempunyai objek TotalTime , tetapi kita memerlukan objek Masa atau sebaliknya?"

"Kami boleh menulis kelas penyesuai untuk ini. Contohnya:"

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 penyesuai 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. Suka. Tapi ada contoh tak?"

"Sudah tentu! Sebagai contoh, InputStreamReader ialah penyesuai klasik. Ia menukarkan InputStream kepada Pembaca."

"Kadang-kadang corak ini juga dipanggil pembungkus, kerana kelas baru 'membungkus' objek lain."

"Anda boleh membaca beberapa perkara menarik lain di sini ."

Corak proksi

"Corak proksi agak serupa dengan corak pembalut. Tetapi tujuannya bukan untuk menukar antara muka, tetapi untuk mengawal akses kepada objek asal yang disimpan di dalam kelas proksi. Selain itu, kedua-dua kelas asal dan proksi biasanya mempunyai antara muka yang sama, yang menjadikannya lebih mudah untuk menggantikan objek kelas asal dengan objek proksi."

"Sebagai contoh:"

Antara muka kelas sebenar
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
Pelaksanaan kelas asal
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
Pelaksanaan kelas proksi
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 menerangkan antara muka Bank dan kelas CitiBank , pelaksanaan antara muka ini."

"Antara muka membolehkan anda mendapatkan atau menukar baki akaun pengguna."

Dan kemudian kami mencipta BankSecurityProxy , yang turut melaksanakan antara muka Bank dan menyimpan rujukan kepada antara muka Bank yang berbeza. Kaedah kelas ini menyemak sama ada pengguna ialah pemilik akaun atau pengurus bank. Jika tidak, maka SecurityException dilemparkan."

"Begini cara ia berfungsi dalam amalan:"

Kod tanpa semakan keselamatan:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
Kod dengan semakan keselamatan:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

"Dalam contoh pertama, kami mencipta objek bank dan memanggil kaedah setUserMoneynya .

"Dalam contoh kedua, kami membungkus objek bank asal dalam objek BankSecurityProxy . Ia mempunyai antara muka yang sama, jadi kod berikutnya terus berfungsi seperti yang dilakukan. Tetapi kini pemeriksaan keselamatan akan dilakukan setiap kali kaedah dipanggil."

"Sejuk!"

"Ya. Anda boleh mempunyai banyak proksi sedemikian. Contohnya, anda boleh menambah proksi lain yang menyemak sama ada baki akaun terlalu besar. Pengurus bank mungkin memutuskan untuk memasukkan banyak wang dalam akaunnya sendiri dan melarikan diri ke Cuba dengan dana itu. ."

"Apatah lagi... Penciptaan semua rantaian objek ini boleh dimasukkan ke dalam kelas BankFactory , di mana anda boleh mendayakan/melumpuhkan objek yang anda perlukan."

" BufferedReader berfungsi menggunakan prinsip yang sama. Ia adalah Pembaca , tetapi ia melakukan kerja tambahan."

"Pendekatan ini membolehkan anda «mengumpul» objek dengan fungsi yang diperlukan daripada pelbagai «kepingan»."

"Oh, saya hampir terlupa. Proksi digunakan lebih meluas daripada apa yang saya baru tunjukkan kepada anda. Anda boleh membaca tentang kegunaan lain di sini ."

Corak jambatan

Corak: Penyesuai, Proksi, Jambatan - 2

"Kadang-kadang semasa program berjalan adalah perlu untuk mengubah fungsi objek dengan ketara. Sebagai contoh, katakan anda mempunyai permainan dengan watak keldai yang kemudiannya bertukar menjadi naga oleh ahli sihir. Naga mempunyai tingkah laku dan sifat yang berbeza, tetapi ia adalah objek yang sama!"

"Tidak bolehkah kita mencipta objek baru dan selesai dengannya?"

"Tidak selalu. Katakan keldai anda berkawan dengan sekumpulan watak, atau mungkin ia berada di bawah pengaruh beberapa mantra, atau ia terlibat dalam pencarian tertentu. Dalam erti kata lain, objek itu mungkin sudah digunakan di banyak tempat — dan dipautkan kepada banyak objek lain. Jadi dalam kes ini, ia bukan pilihan untuk mencipta objek baharu sahaja."

"Nah, apa yang boleh dibuat kemudian?"

"Corak Jambatan adalah salah satu penyelesaian yang paling berjaya."

"Corak ini memerlukan pemisahan objek kepada dua objek: «objek antara muka» dan «objek pelaksanaan»."

"Apakah perbezaan antara antara muka dan kelas yang melaksanakannya?"

"Dengan antara muka dan kelas, kita berakhir dengan satu objek. Tetapi di sini — kita mempunyai 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 boleh mengisytiharkan beberapa subkelas UserImpl, contohnya UserDonkey (keldai) dan UserDragon (naga)."

"Walaupun begitu, saya tidak begitu faham bagaimana ini akan berfungsi."

"Nah, 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 ia berfungsi
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

"Jadi ia adalah sesuatu seperti proksi."

"Ya, tetapi dalam proksi objek utama boleh disimpan di suatu tempat secara berasingan, dan kod itu berfungsi dengan proksi sebaliknya. Di sini kami mengatakan bahawa semua orang berfungsi dengan objek utama, tetapi bahagiannya berubah secara dalaman."

"Ah. Terima kasih. Sudikah anda memberi saya pautan untuk membaca lebih lanjut mengenainya?"

"Sudah tentu, Amigo, kawan saya. Ini dia: Corak jambatan ."