"Chào bạn!"

"Chào, Bilaabo!"

"Chúng ta vẫn còn một chút thời gian, vì vậy tôi sẽ nói với bạn về ba mẫu nữa."

"Ba cái nữa? Có bao nhiêu tất cả?"

"Hiện tại có hàng tá mẫu phổ biến, nhưng số lượng «giải pháp thành công» là không giới hạn."

"Tôi hiểu rồi. Vậy tôi phải học mấy chục mẫu?"

"Cho đến khi bạn có kinh nghiệm lập trình thực sự, họ sẽ không cung cấp cho bạn nhiều."

"Tốt hơn là bạn nên có thêm một chút kinh nghiệm, và sau đó, sau một năm, hãy quay lại chủ đề này và cố gắng hiểu chúng sâu hơn. Ít nhất là vài chục mẫu thiết kế phổ biến nhất."

"Thật tội lỗi nếu không sử dụng kinh nghiệm của người khác và thay vào đó phát minh ra thứ gì đó lần thứ 110."

"Tôi đồng ý."

"Vậy chúng ta bắt đầu đi."

Mẫu bộ điều hợp (hoặc trình bao bọc)

Các mẫu: Bộ điều hợp, Proxy, Cầu nối - 1

"Hãy tưởng tượng bạn đến Trung Quốc và thấy rằng các ổ cắm điện tuân theo một tiêu chuẩn khác. Các lỗ không tròn mà phẳng. Trong trường hợp này, bạn sẽ cần một bộ chuyển đổi."

"Điều tương tự cũng có thể xảy ra trong lập trình. Các lớp hoạt động trên các giao diện tương tự nhưng khác nhau. Vì vậy, chúng ta cần tạo một bộ điều hợp giữa chúng."

"Cái này nó thì trông như thế nào:"

Ví dụ
interface Time
{
 int getSeconds();
 int getMinutes();
 int getHours();
}

interface TotalTime
{
 int getTotalSeconds();
}

"Giả sử chúng ta có hai giao diện: Thời gian  và  Tổng thời gian ."

"Giao diện Thời gian cho phép bạn lấy thời gian hiện tại bằng cách sử dụng các phương thức getSeconds (),  getMinutes () và  getHours ()."

" Giao diện TotalTime cho phép bạn xem số giây đã trôi qua từ nửa đêm đến thời điểm hiện tại."

"Chúng ta nên làm gì nếu chúng ta có một đối tượng TotalTime , nhưng chúng ta cần một đối tượng Time hoặc ngược lại?"

"Chúng ta có thể viết các lớp bộ điều hợp cho việc này. Ví dụ:"

Ví dụ
 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
 }
}
 
Cách sử dụng
TotalTime totalTime = TimeManager.getCurrentTime();
Time time = new TotalTimeAdapter(totalTime);
System.out.println(time.getHours() + " : " + time.getMinutes () + " : " +time.getSeconds());

"Và một bộ chuyển đổi theo hướng khác:"

Ví dụ
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();
 }
}
Cách sử dụng
Time time = new Time();
TotalTime totalTime = new TimeAdapter(time);
System.out.println(time.getTotalSeconds());

"À. Tôi thích nó. Nhưng có ví dụ nào không?"

"Tất nhiên! Ví dụ: InputStreamReader là một bộ điều hợp cổ điển. Nó chuyển đổi InputStream thành Reader."

"Đôi khi mẫu này còn được gọi là trình bao bọc, bởi vì lớp mới 'bao bọc' một đối tượng khác."

"Bạn có thể đọc một số điều thú vị khác ở đây ."

Mẫu ủy quyền

"Mẫu proxy hơi giống với mẫu bao bọc. Nhưng mục đích của nó không phải là chuyển đổi giao diện, mà là kiểm soát quyền truy cập vào đối tượng gốc được lưu trữ bên trong lớp proxy. Hơn nữa, cả lớp gốc và proxy thường có cùng một giao diện, giúp dễ dàng thay thế một đối tượng của lớp ban đầu bằng một đối tượng proxy."

"Ví dụ:"

Giao diện của lớp thực
interface Bank
{
 public void setUserMoney(User user, double money);
 public int getUserMoney(User user);
}
Triển khai lớp ban đầu
class CitiBank implements Bank
{
 public void setUserMoney(User user, double money)
 {
  UserDAO.updateMoney(user, money);
 }

 public int getUserMoney(User user)
 {
  return UserDAO.getMoney(user);
 }
}
Triển khai lớp 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);
 }
}

"Trong ví dụ trên, chúng tôi đã mô tả giao diện Ngân hàng và lớp CitiBank , một triển khai của giao diện này."

"Giao diện cho phép bạn lấy hoặc thay đổi số dư tài khoản của người dùng."

Và sau đó, chúng tôi đã tạo BankSecurityProxy , cũng triển khai giao diện Ngân hàng và lưu trữ tham chiếu đến giao diện Ngân hàng khác. Các phương thức của lớp này kiểm tra xem người dùng là chủ tài khoản hay người quản lý ngân hàng. Nếu không, thì SecurityException sẽ bị ném."

"Đây là cách nó hoạt động trong thực tế:"

không có kiểm tra bảo mật:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank.setUserMoney(user, 1000000);
kiểm tra bảo mật:
User user = AuthManager.authorize(login, password);
Bank bank = BankFactory.createUserBank(user);
bank = new BankSecurityProxy(bank);
bank.setUserMoney(user, 1000000);

"Trong ví dụ đầu tiên, chúng tôi tạo một đối tượng ngân hàng và gọi phương thức setUserMoney của nó .

"Trong ví dụ thứ hai, chúng tôi bọc đối tượng ngân hàng ban đầu trong một đối tượng BankSecurityProxy . Chúng có cùng giao diện, vì vậy mã tiếp theo tiếp tục hoạt động như trước đây. Nhưng giờ đây, kiểm tra bảo mật sẽ được thực hiện mỗi khi một phương thức được gọi."

"Mát mẻ!"

"Đúng. Bạn có thể có nhiều proxy như vậy. Ví dụ: bạn có thể thêm một proxy khác để kiểm tra xem số dư tài khoản có quá lớn hay không. Người quản lý ngân hàng có thể quyết định bỏ rất nhiều tiền vào tài khoản của chính mình và trốn sang Cuba cùng với số tiền đó ."

"Hơn nữa... Việc tạo ra tất cả các chuỗi đối tượng này có thể được đưa vào một lớp BankFactory , nơi bạn có thể bật/tắt những thứ bạn cần."

" BufferedReader hoạt động bằng cách sử dụng nguyên tắc tương tự. Đó là Reader , nhưng nó thực hiện công việc bổ sung."

"Cách tiếp cận này cho phép bạn «lắp ráp» một đối tượng với chức năng cần thiết từ nhiều «phần» khác nhau."

"Ồ, tôi suýt quên mất. Proxy được sử dụng rộng rãi hơn nhiều so với những gì tôi vừa chỉ cho bạn. Bạn có thể đọc về những cách sử dụng khác tại đây ."

mẫu cầu

Các mẫu: Bộ điều hợp, Proxy, Cầu nối - 2

"Đôi khi một chương trình đang chạy, cần phải thay đổi đáng kể chức năng của một đối tượng. Ví dụ: giả sử bạn có một trò chơi với một nhân vật lừa sau đó bị một pháp sư biến thành rồng. Con rồng có hành vi và đặc tính hoàn toàn khác, nhưng nó cùng một đối tượng!"

"Chúng ta không thể tạo một đối tượng mới và hoàn thành nó sao?"

"Không phải lúc nào cũng vậy. Giả sử con lừa của bạn là bạn của một nhóm nhân vật, hoặc có lẽ nó đang chịu ảnh hưởng của một vài câu thần chú, hoặc nó đã tham gia vào một số nhiệm vụ. Nói cách khác, đồ vật có thể đã được sử dụng ở nhiều nơi — và được liên kết với nhiều đối tượng khác. Vì vậy, trong trường hợp này, việc tạo một đối tượng mới không phải là một tùy chọn."

"Chà, vậy thì có thể làm gì?"

"Mẫu Cầu là một trong những giải pháp thành công nhất."

"Mẫu này đòi hỏi phải chia một đối tượng thành hai đối tượng: một «đối tượng giao diện» và một «đối tượng triển khai»."

"Sự khác biệt giữa giao diện và lớp thực hiện nó là gì?"

"Với một giao diện và một lớp, chúng ta có một đối tượng. Nhưng ở đây — chúng ta có hai đối tượng. Hãy xem ví dụ này:"

Ví dụ
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()
 {
 }
}

"Và sau đó, bạn có thể khai báo một số lớp con của UserImpl, ví dụ UserDonkey (lừa) và UserDragon (rồng)."

"Dù sao đi nữa, tôi thực sự không hiểu nó sẽ hoạt động như thế nào."

"Chà, đại loại thế này:"

Ví dụ
class User
{
 private UserImpl realUser;

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

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

 public void transformToDragon()
 {
  realUser = new UserDragonImpl();
 }
}
Làm thế nào nó hoạt động
User user = new User(new UserDonkey()); // Internally, we're a donkey
user.transformToDragon(); // Now we're a dragon internally

"Vì vậy, nó là một cái gì đó giống như một proxy."

"Vâng, nhưng trong một proxy, đối tượng chính có thể được lưu trữ ở đâu đó riêng biệt và thay vào đó, mã sẽ hoạt động với proxy. Ở đây chúng tôi đang nói rằng mọi người đều làm việc với đối tượng chính, nhưng các bộ phận của nó thay đổi bên trong."

"À. Cảm ơn. Bạn có thể cho tôi một liên kết để đọc thêm về nó không?"

"Tất nhiên, Amigo, bạn của tôi. Đây: Mẫu cầu ."