Làm cách nào để sử dụng mẫu Trang trí để thêm ít chức năng cho các đối tượng lớn?


8

Câu hỏi này liên quan đến việc sử dụng mẫu Trình trang trí để thêm ít chức năng cho các đối tượng của các lớp lớn.

Theo mô hình Trang trí cổ điển, vui lòng xem xét cấu trúc lớp sau:

nhập mô tả hình ảnh ở đây

Ví dụ, hãy tưởng tượng điều này xảy ra trong một trò chơi. Trường hợp ConcreteCharacterDecoratorcó nghĩa là để thêm ít chức năng cho ConcreteCharacterhọ đang 'gói'.

Chẳng hạn, methodA()trả về một intgiá trị đại diện cho thiệt hại mà nhân vật gây ra cho kẻ thù. Đơn ConcreteCharacterDecoratorgiản chỉ cần thêm vào giá trị này. Vì vậy, nó chỉ cần thêm mã vào methodA(). Các chức năng của methodB()vẫn giữ nguyên.

ConcreteCharacterDecorator sẽ trông như thế này:

class ConcreteCharacterDecorator extends AbstractCharacterDecorator{

    ConcreteCharacter character;

    public ConcreteCharacterDecorator(ConcreteCharacter character){
        this.character = character;
    }

    public int methodA(){
        return 10 + character.methodA();
    }

    public int methodB(){
        character.methodB(); // simply delegate to the wrapped object.
    }

}

Điều này không có vấn đề với các lớp nhỏ chứa hai phương thức.

Nhưng nếu AbstractCharacterxác định 15 phương thức thì sao? ConcreteCharacterDecoratorsẽ phải thực hiện tất cả chúng, mặc dù nó chỉ có nghĩa là thêm ít chức năng.

Tôi sẽ kết thúc với một lớp chứa một phương thức có thêm một ít chức năng và 14 phương thức khác chỉ đơn giản là ủy thác cho đối tượng bên trong.

Nó sẽ trông như vậy:

class ConcreteCharacterDecorator extends AbstractCharacterDecorator{

    ConcreteCharacter character;

    public ConcreteCharacterDecorator(ConcreteCharacter character){
        this.character = character;
    }

    public int methodA(){
        return 10 + character.methodA();
    }

    public int methodB(){
        character.methodB(); // simply delegate to the wrapped object.
    }
    public int methodC(){
        character.methodC(); // simply delegate to the wrapped object.
    }
    public int methodD(){
        character.methodD(); // simply delegate to the wrapped object.
    }
    public int methodE(){
        character.methodE(); // simply delegate to the wrapped object.
    }
    public int methodF(){
        character.methodF(); // simply delegate to the wrapped object.
    }
    public int methodG(){
        character.methodG(); // simply delegate to the wrapped object.
    }
    public int methodH(){
        character.methodH(); // simply delegate to the wrapped object.
    }
    public int methodI(){
        character.methodI(); // simply delegate to the wrapped object.
    }
    public int methodJ(){
        character.methodJ(); // simply delegate to the wrapped object.
    }
    public int methodK(){
        character.methodK(); // simply delegate to the wrapped object.
    }
    public int methodL(){
        character.methodL(); // simply delegate to the wrapped object.
    }
    public int methodM(){
        character.methodM(); // simply delegate to the wrapped object.
    }
    public int methodN(){
        character.methodN(); // simply delegate to the wrapped object.
    }
    public int methodO(){
        character.methodO(); // simply delegate to the wrapped object.
    }

}

Rõ ràng, rất xấu xí.

Tôi có lẽ không phải là người đầu tiên gặp phải vấn đề này với Người trang trí. Làm thế nào tôi có thể tránh điều này?

Câu trả lời:


6

Nhanh chóng và dễ dàng: AbstractCharacterDelegatorkhông có chức năng trừu tượng, nhưng thay vào đó là ảo. Theo mặc định, chúng chuyển qua ký tự được bọc và có thể bị ghi đè. Sau đó, bạn kế thừa và chỉ ghi đè lên số ít bạn muốn.

Điều này không đặc biệt hữu ích trừ khi bạn trang trí khá nhiều và giao diện chung của bạn đặc biệt rộng. Điều đó không nên quá phổ biến. Và cách tiếp cận này có thể không phải bằng chữ cái một người trang trí, nhưng nó sử dụng khái niệm này và sẽ được hiểu là một người trang trí bởi các lập trình viên biết mẫu.


Vì vậy, bạn đang đề xuất một cách đơn giản - trong Java - làm cho các phương thức trong AbstractCharacterDelegator'chính quy' (vì tất cả các phương thức 'thông thường' trong Java là ảo theo mặc định - có nghĩa là chúng có thể bị ghi đè bằng cách kế thừa các lớp con)? Và có thực hiện chúng chỉ đơn giản là ủy thác cho đối tượng bên trong - và nếu tôi muốn thay đổi điều đó, chỉ cần ghi đè chúng?
Manila Cohn

Nếu vậy, điều đó có nghĩa là thực sự tôi sẽ phải sử dụng các lớp trừu tượng chứ không phải các giao diện, vì các giao diện không cho phép thực hiện trong các phương thức. Vì vậy, tôi sẽ có mã sạch hơn, nhưng không có 'nhiều kế thừa' với các giao diện. Cái nào quan trọng hơn?
Manila Cohn

@Prog Vâng, đó là những gì anh ấy nói. Nó không loại trừ bằng cách sử dụng một giao diện. Quay trở lại sơ đồ UML, bạn có thể biến AbstractCharacterthành một interface. Phần còn lại vẫn giữ nguyên - ConcreteCharacterAbstractCharacterDecoratorthực hiện giao diện thay vì phân lớp. Mọi thứ chỉ nên làm việc. Mặc dù họ có thể không cần phải mở rộng bất cứ điều gì khác, nhưng các nhà trang trí của bạn không bị khóa trong phân lớp AbstractCharacterDecoratorvì họ luôn có thể thực hiện nhiệm vụ theo cách cũ nếu họ cần phải mở rộng một lớp khác vì một số lý do.
Doval

@Doval Về cơ bản, cách tiếp cận tốt nhất sẽ là có giao diện AbstractCharacter và mọi thứ khác cụ thể hoặc trừu tượng. Điều này sẽ cho phép mã sạch, như Telastyn nói, nhưng để linh hoạt hơn việc sử dụng tất cả các lớp trừu tượng. Đúng?
Manila Cohn

1
@Prog Vâng, đó là về tổng hợp nó.
Doval

3

Nếu AbstractCharacter có 15 phương thức, có lẽ nó cần được chia thành các lớp nhỏ hơn. Ngoài ra, bạn không thể làm gì khác nếu không có ngôn ngữ cung cấp một số loại cú pháp hỗ trợ đặc biệt cho việc ủy ​​quyền. Đơn giản chỉ cần để lại một bình luận trong đó tất cả các phái đoàn bắt đầu để người đọc biết anh ta không cần nhìn xuống nữa. Đối với việc viết tất cả các phương thức chuyển tiếp, IDE của bạn có thể giúp đỡ ở đó.

Ngoài ra, theo ý kiến ​​của tôi, Decorator hoạt động tốt nhất với các giao diện. Nói chung, bạn muốn giới hạn sự trừu tượng của mình ở một số lượng các lớp con hữu hạn (có thể là 0) hoặc đi tất cả các cách với một giao diện. Kế thừa không giới hạn có tất cả các nhược điểm của giao diện (bạn có thể được thông qua triển khai không chính xác) nhưng bạn không có được sự linh hoạt hoàn toàn (bạn chỉ có thể phân lớp một thứ.) Tệ hơn nữa, nó mở ra cho bạn vấn đề Lớp cơ sở mong manh và nó không linh hoạt như thành phần (bạn tạo SubgroupA và SubgroupB, nhưng sau đó không thể kế thừa từ cả hai để tạo SubgroupAB với các tính năng của cả hai). Vì vậy, tôi thấy bị buộc vào một cây thừa kế chỉ để hỗ trợ trang trí trở thành một loại bùn.


+1 để nêu rõ vấn đề gắn kết (quá nhiều trách nhiệm là 15 phương thức) và sử dụng giao diện.
Fuhrmanator 22/03 '

Đôi khi nó không thể được chia thành các lớp nhỏ hơn, giả sử một lớp hệ thống tệp.
Vicente Bolea
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.