Newbie câu hỏi về mẫu thiết kế trang trí


18

Tôi đã đọc một bài viết lập trình và nó đã đề cập đến mẫu Decorator. Tôi đã lập trình được một lúc nhưng không có bất kỳ loại giáo dục hay đào tạo chính thức nào, nhưng tôi đang cố gắng tìm hiểu về các mẫu chuẩn và như vậy.

Vì vậy, tôi đã tra cứu Trình trang trí và tìm thấy một bài viết trên Wikipedia về nó. Bây giờ tôi hiểu khái niệm về mẫu Trang trí, nhưng tôi hơi bối rối với đoạn văn này:

Ví dụ, xem xét một cửa sổ trong một hệ thống cửa sổ. Để cho phép cuộn nội dung của cửa sổ, chúng tôi có thể muốn thêm thanh cuộn ngang hoặc dọc vào nó, nếu thích hợp. Giả sử các cửa sổ được đại diện bởi các thể hiện của lớp Window và giả sử lớp này không có chức năng để thêm thanh cuộn. Chúng ta có thể tạo một lớp con ScrollingWindow cung cấp cho chúng hoặc chúng ta có thể tạo một ScrollingWindowDecorator bổ sung chức năng này cho các đối tượng Window hiện có. Tại thời điểm này, một trong hai giải pháp sẽ ổn.

Bây giờ hãy giả sử rằng chúng tôi cũng mong muốn khả năng thêm viền vào cửa sổ của chúng tôi. Một lần nữa, lớp Window ban đầu của chúng tôi không có hỗ trợ. Lớp con ScrollingWindow hiện đặt ra một vấn đề, bởi vì nó đã tạo ra một loại cửa sổ mới một cách hiệu quả. Nếu chúng tôi muốn thêm hỗ trợ viền cho tất cả các cửa sổ, chúng tôi phải tạo các lớp con WindowWithBorder và ScrollingWindowWithBorder. Rõ ràng, vấn đề này trở nên tồi tệ hơn với mỗi tính năng mới được thêm vào. Đối với giải pháp trang trí, chúng ta chỉ cần tạo một BorderedWindowDecorator mới trong thời gian chạy, chúng ta có thể trang trí các cửa sổ hiện có với ScrollingWindowDecorator hoặc BorderedWindowDecorator hoặc cả hai, khi chúng ta thấy phù hợp.

OK, khi họ nói thêm viền cho tất cả các cửa sổ, tại sao không thêm chức năng vào lớp Window ban đầu để cho phép tùy chọn? Theo cách tôi thấy, phân lớp chỉ là để thêm chức năng cụ thể vào một lớp hoặc ghi đè một phương thức lớp. Nếu tôi cần thêm chức năng cho tất cả các đối tượng hiện có, tại sao tôi không sửa đổi siêu lớp để làm như vậy?

Có một dòng khác trong bài viết:

Mẫu trang trí là một thay thế cho phân lớp. Phân lớp thêm hành vi tại thời gian biên dịch và thay đổi ảnh hưởng đến tất cả các phiên bản của lớp gốc; trang trí có thể cung cấp hành vi mới trong thời gian chạy cho các đối tượng cá nhân.

Tôi không biết họ nói "... thay đổi ảnh hưởng đến tất cả các trường hợp của lớp ban đầu" - cách phân lớp thay đổi lớp cha? Không phải đó là toàn bộ điểm của phân lớp sao?

Tôi sẽ giả định rằng bài báo, giống như nhiều Wiki, không được viết rõ ràng. Tôi có thể thấy sự hữu ích của Trình trang trí trong dòng cuối cùng đó - "... cung cấp hành vi mới vào thời gian chạy cho các đối tượng riêng lẻ."

Nếu không đọc về mẫu này, nếu tôi cần thay đổi hành vi trong thời gian chạy cho các đối tượng riêng lẻ, tôi có thể đã xây dựng một số phương thức vào siêu lớp hoặc lớp con để bật / tắt hành vi nói. Xin hãy giúp tôi thực sự hiểu được sự hữu ích của Trình trang trí, và tại sao suy nghĩ của người mới của tôi là thiếu sót?


đánh giá cao bản chỉnh sửa, Walter ... fyi, tôi đã sử dụng "các chàng trai" không loại trừ phụ nữ, mà như một lời chào không chính thức.
Jim

Chỉnh sửa sẽ chỉ là để loại bỏ lời chào nói chung. Đây không phải là giao thức SO / SE tiêu chuẩn để sử dụng một trong các câu hỏi [đừng lo lắng, chúng tôi không nghĩ rằng thật thô lỗ khi nhảy thẳng vào câu hỏi]
Farrell

hay quá cảm ơn! chỉ là anh ấy gắn thẻ "xóa giới tính" và tôi ghét bất cứ ai nghĩ rằng tôi là người không chính đáng hay gì đó !! :)
Jim

Tôi sử dụng chúng rất nhiều để tránh những thứ mang tính thủ tục pháp lý gọi là "dịch vụ": Medium.com/@wrong.about/iêu
Zapadlo

Câu trả lời:


13

Mẫu trang trí là một mẫu ủng hộ bố cục hơn thừa kế [một mô hình OOP khác hữu ích để tìm hiểu về]

Lợi ích chính của mẫu trang trí - trên phân lớp là cho phép nhiều tùy chọn kết hợp & kết hợp hơn. Ví dụ, nếu bạn có 10 hành vi khác nhau mà một cửa sổ có thể có, thì điều này có nghĩa là - với phân lớp - bạn cần tạo mọi kết hợp khác nhau, chắc chắn sẽ bao gồm rất nhiều lần sử dụng lại mã.
Tuy nhiên, điều gì xảy ra khi bạn quyết định thêm vào một hành vi mới?

Với trình trang trí, bạn chỉ cần thêm một lớp mới mô tả hành vi này và đó là - mô hình cho phép bạn thả hiệu quả này vào mà không có bất kỳ sửa đổi nào đối với phần còn lại của mã.
Với lớp học phụ, bạn đã có một cơn ác mộng trên tay.
Một câu hỏi bạn đặt ra là "làm thế nào để phân lớp thay đổi lớp cha?" Không phải là nó thay đổi lớp cha; khi nó nói một thể hiện, nó có nghĩa là bất kỳ đối tượng nào bạn đã 'khởi tạo' [nếu bạn đang sử dụng Java hoặc C #, bằng cách sử dụng newlệnh]. Điều mà nó đề cập đến là, khi bạn thêm những thay đổi này vào một lớp, bạn không có lựa chọn nào cho sự thay đổi đó ở đó, ngay cả khi bạn không thực sự cần nó.

Ngoài ra, bạn có thể đặt tất cả chức năng của anh ấy vào một lớp duy nhất với nó được bật / tắt thông qua cờ ... nhưng điều này kết thúc với một lớp duy nhất trở nên lớn hơn và lớn hơn khi dự án của bạn phát triển.
Không có gì lạ khi bắt đầu dự án của bạn theo cách này và tái cấu trúc thành một mô hình trang trí một khi bạn đạt được một khối lượng quan trọng hiệu quả.

Một điểm thú vị nên được thực hiện: bạn có thể thêm chức năng tương tự nhiều lần; do đó, bạn có thể có một cửa sổ với số lượng gấp đôi, gấp ba hoặc bất kỳ số lượng hoặc đường viền nào bạn yêu cầu.

Điểm chính của mẫu là cho phép thay đổi thời gian chạy: bạn có thể không biết bạn muốn cửa sổ trông như thế nào cho đến khi chương trình đang chạy và điều này cho phép bạn dễ dàng sửa đổi nó. Cấp, điều này có thể được thực hiện thông qua phân lớp, nhưng không phải là độc đáo.
Và cuối cùng, nó cho phép chức năng được thêm vào các lớp mà bạn không thể chỉnh sửa - ví dụ như trong các lớp kín / cuối cùng, hoặc các lớp được cung cấp từ các API khác


2
Cảm ơn, Farrell - khi bạn nói "Ngoài ra, bạn có thể đặt tất cả chức năng của anh ấy vào một lớp duy nhất với nó được bật / tắt thông qua cờ ... nhưng điều này kết thúc với một lớp duy nhất trở nên lớn hơn và lớn hơn khi dự án của bạn phát triển." Điều đó làm cho nó nhấp cho tôi. Tôi nghĩ một phần của vấn đề là tôi chưa bao giờ làm việc trong một lớp học quá lớn mà người trang trí có ý nghĩa với tôi. Suy nghĩ lớn, tôi chắc chắn có thể thấy những lợi ích ... cảm ơn!
Jim

Ngoài ra, phần về các lớp niêm phong làm cho rất nhiều ý nghĩa ... cảm ơn!
Jim

3

Xem xét khả năng thêm thanh cuộn và đường viền với phân lớp. Nếu bạn muốn mọi khả năng, bạn có bốn lớp (Python):

class Window(object):
    def draw(self):
        "do actual drawing of window"

class WindowWithScrollBar(Window):
    def draw(self):
        Window.draw(self)
        "do actual drawing of scrollbar"

class WindowWithBorder(Window):
    def draw(self):
        Window.draw(self)
        "do actual drawing of border"

class WindowWithScrollBarAndBorder(Window):
    def draw(self):
        WindowWithScrollBar.draw(self)
        WindowWithBorder.draw(self)

Bây giờ, trong WindowWithScrollBarAndBorder.draw(), cửa sổ được vẽ hai lần và lần thứ hai nó có thể hoặc không ghi đè lên Thanh cuộn đã vẽ, tùy thuộc vào việc triển khai. Vì vậy, mã dẫn xuất của bạn được kết hợp chặt chẽ với việc triển khai một lớp khác và bạn phải quan tâm đến điều đó mỗi khi bạn thay đổi Windowhành vi của lớp. Một giải pháp sẽ là sao chép-dán mã từ các siêu lớp tho các lớp dẫn xuất và điều chỉnh nó theo nhu cầu của các lớp dẫn xuất, nhưng mọi thay đổi trong một siêu lớp phải được dán lại và điều chỉnh lại, vì vậy một lần nữa các lớp dẫn xuất lại kết hợp chặt chẽ với lớp cơ sở (thông qua nhu cầu sao chép-dán-điều chỉnh). Một vấn đề khác là nếu bạn cần một thuộc tính khác mà một cửa sổ có thể có hoặc không có, bạn phải tăng gấp đôi mỗi lớp:

class Window(object):
    ...

class WindowWithGimmick(Window):
    ...

class WindowWithScrollBar(Window):
    ...

class WindowWithBorder(Window):
    ...

class WindowWithScrollBarAndBorder(Window):
    ...

class WindowWithScrollBarAndGimmick(Window):
    ...

class WindowWithBorderAndGimmick(Window):
    ...

class WindowWithScrollBarAndBorderAndGimmick(Window):
    ...

Điều đó có nghĩa là cho một tập hợp các tính năng độc lập lẫn nhau f với | f | là số lượng tính năng, bạn phải xác định 2 ** | f | các lớp học, vd. nếu bạn có 10 tính năng, bạn sẽ nhận được 1024 lớp bị khóa chặt. Nếu bạn sử dụng Mẫu trang trí, mọi tính năng đều có lớp kết hợp độc lập và lỏng lẻo của riêng nó và bạn chỉ có 1 + | f | các lớp (đó là 11 ví dụ ở trên).


Hãy xem, suy nghĩ của tôi sẽ không tiếp tục thêm các lớp - đó sẽ là thêm tính tự do vào lớp (phụ) ban đầu. Vì vậy, trong cửa sổ lớp (đối tượng): bạn có scrollBar = true, Border = false, v.v ... Câu trả lời Farrell chỉ cho tôi nơi có thể sai, tuy nhiên - chỉ dẫn đến các lớp cồng kềnh và ... cảm ơn vì câu trả lời, +1!
Jim

tốt, +1 khi tôi có đại diện để làm như vậy!
Jim

2

Tôi không phải là chuyên gia về mẫu cụ thể này, nhưng như tôi thấy, mẫu Trang trí có thể được áp dụng cho các lớp mà bạn có thể không có khả năng sửa đổi hoặc lớp phụ (ví dụ, chúng có thể không phải là mã của bạn và được niêm phong ). Trong ví dụ của bạn, điều gì sẽ xảy ra nếu bạn không viết lớp Window mà chỉ tiêu thụ nó? Miễn là lớp Window có giao diện và bạn lập trình theo giao diện đó, Trình trang trí của bạn có thể sử dụng cùng một giao diện nhưng mở rộng chức năng.

Ví dụ bạn đề cập thực sự được đề cập ở đây khá gọn gàng:

Việc mở rộng chức năng của một đối tượng có thể được thực hiện tĩnh (tại thời gian biên dịch) bằng cách sử dụng tính kế thừa tuy nhiên có thể cần phải mở rộng chức năng của đối tượng một cách linh hoạt (khi chạy) khi một đối tượng được sử dụng.

Hãy xem xét ví dụ điển hình của một cửa sổ đồ họa. Ví dụ, để mở rộng chức năng của cửa sổ đồ họa bằng cách thêm khung vào cửa sổ, sẽ yêu cầu mở rộng lớp cửa sổ để tạo lớp framesWindow. Để tạo một cửa sổ có khung, cần phải tạo một đối tượng của lớp framesWindow. Tuy nhiên, không thể bắt đầu với một cửa sổ đơn giản và mở rộng chức năng của nó khi chạy để trở thành một cửa sổ đóng khung.

http://www.oodesign.com/decorator-potype.html

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.