Hiểu mô hình trang trí của người Viking với một ví dụ thực tế


167

Tôi đã nghiên cứu Mẫu trang trí như được ghi lại trong GOF .

Xin vui lòng, giúp tôi hiểu các mẫu trang trí . Ai đó có thể đưa ra một ví dụ trường hợp sử dụng về nơi điều này hữu ích trong thế giới thực?


8
Bạn có thể tìm thấy ở đây một số ví dụ về thế giới thực trong API Java: stackoverflow.com/questions/1673841/iêu
BalusC

Một bài viết cho thấy những lợi ích của mẫu trang trí với các ví dụ đơn giản: dzone.com/articles/is-inherribution-dead
nbilal 8/12/2016

Câu trả lời:


226

Mẫu trang trí đạt được một mục tiêu duy nhất là tự động thêm trách nhiệm cho bất kỳ đối tượng nào.

Hãy xem xét một trường hợp của một cửa hàng pizza. Trong cửa hàng pizza, họ sẽ bán một vài loại bánh pizza và họ cũng sẽ cung cấp toppings trong thực đơn. Bây giờ hãy tưởng tượng một tình huống trong đó nếu cửa hàng pizza phải cung cấp giá cho mỗi sự kết hợp giữa pizza và topping. Ngay cả khi có bốn loại pizza cơ bản và 8 loại toppings khác nhau, ứng dụng sẽ phát điên khi duy trì tất cả các kết hợp cụ thể này của pizza và toppings.

Đây là mẫu trang trí.

Theo mô hình trang trí, bạn sẽ thực hiện toppings vì trang trí và pizza sẽ được trang trí bởi các trang trí của toppings. Thực tế, mỗi khách hàng sẽ muốn đạt được mong muốn của mình và số tiền hóa đơn cuối cùng sẽ bao gồm các loại pizza cơ bản và các loại toppings được đặt hàng bổ sung. Mỗi nhà trang trí đứng đầu sẽ biết về các loại pizza mà nó đang trang trí và giá của nó. Phương thức Topping của đối tượng Getprice () sẽ trả về giá tích lũy của cả pizza và topping.

BIÊN TẬP

Đây là một ví dụ mã giải thích ở trên.

public abstract class BasePizza
{
    protected double myPrice;

    public virtual double GetPrice()
    {
        return this.myPrice;
    }
}

public abstract class ToppingsDecorator : BasePizza
{
    protected BasePizza pizza;
    public ToppingsDecorator(BasePizza pizzaToDecorate)
    {
        this.pizza = pizzaToDecorate;
    }

    public override double GetPrice()
    {
        return (this.pizza.GetPrice() + this.myPrice);
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //Client-code
        Margherita pizza = new Margherita();
        Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

        ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
        ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

        MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

        JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

        Console.ReadLine();
    }
}

public class Margherita : BasePizza
{
    public Margherita()
    {
        this.myPrice = 6.99;
    }
}

public class Gourmet : BasePizza
{
    public Gourmet()
    {
        this.myPrice = 7.49;
    }
}

public class ExtraCheeseTopping : ToppingsDecorator
{
    public ExtraCheeseTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 0.99;
    }
}

public class MushroomTopping : ToppingsDecorator
{
    public MushroomTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

public class JalapenoTopping : ToppingsDecorator
{
    public JalapenoTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

104
Không thích mẫu này một chút. Có lẽ đó là ví dụ. Vấn đề chính mà tôi có với nó về vấn đề của 3M là topping không phải là pizza . Yêu cầu đứng đầu về giá của bánh pizza, nó được áp dụng để không ngồi đúng với tôi. Đó là một ví dụ rất chu đáo và chi tiết, vì vậy tôi không có ý đánh gục bạn vì điều đó.
Tom W

39
@TomW Tôi nghĩ một phần của vấn đề là việc đặt tên. Tất cả các lớp "Topping" nên được gọi là "PizzaWith <Topping>". Ví dụ: "PizzaWithMushroom".
Josh Noe

2
Theo tôi, trang trí tốt nhất được sử dụng càng phẳng càng tốt. Điều đó có nghĩa là tôi càng ít "trang trí gói trang trí" càng tốt. Vì vậy, có lẽ ví dụ này không phải là thích hợp nhất. Nhưng nó là khá kỹ lưỡng, đó là tốt đẹp.
thekingoftruth

17
Từ góc độ khác, điều này thậm chí không gần với "thế giới thực". Trong thế giới thực, bạn không nên biên dịch lại mỗi lần bạn cần thêm một topping mới trong menu (hoặc thay đổi giá). Toppings (thường) được lưu trữ trong cơ sở dữ liệu và do đó làm cho ví dụ trên trở nên vô dụng.
Stelios Adamantidis

4
^ Cái này Tôi nghĩ rằng đây là những gì đã làm phiền tôi trong khi nghiên cứu mô hình này. Nếu tôi là một công ty phần mềm và viết phần mềm cửa hàng pizza, tôi sẽ không muốn phải biên dịch lại và bán lại mỗi lần. Tôi muốn thêm một hàng trong một bảng trong phần phụ trợ hoặc thứ gì đó có thể dễ dàng đáp ứng yêu cầu của họ. Nói tốt, @Stelios Adamantidis. Tôi đoán rằng mô hình sức mạnh lớn nhất mặc dù sẽ sửa đổi các lớp bên thứ 3 sau đó.
Canucklesandwich

33

Đây là một ví dụ đơn giản về việc thêm hành vi mới vào đối tượng hiện có một cách linh hoạt hoặc mẫu Trang trí. Do bản chất của các ngôn ngữ động như Javascript, mẫu này trở thành một phần của chính ngôn ngữ.

// Person object that we will be decorating with logging capability
var person = {
  name: "Foo",
  city: "Bar"
};

// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
  object.log = function(property) {
    console.log(this[property]);
  }
}

// Person is given the dynamic responsibility here
MakeLoggable(person);

// Using the newly added functionality
person.log('name');


Đơn giản và chính xác! Ví dụ tuyệt vời!
nagendra547

1
Tôi không nghĩ rằng khái niệm Mẫu trang trí có thể áp dụng ở đây. Thật ra nó không phải là một mô hình nào cả!. Có, Bạn đang thêm một phương thức mới trong thời gian chạy. Và có lẽ bên trong một switchhoặc đơn giản if, bạn sẽ có thể khẳng định rằng đây là một ví dụ tuyệt vời về việc thêm động một cách linh hoạt vào một lớp. NHƯNG, chúng ta cần ít nhất hai lớp để định nghĩa một trang trí và các đối tượng được trang trí theo mô hình này.
Iman

1
@ Tôi hiểu rằng không có trình trang trí nào trong ví dụ của tôi nhưng điều đó dễ dàng được khắc phục bằng cách thêm một chức năng phục vụ như một trình trang trí. Nhưng có một đối tượng trang trí trong ví dụ của tôi. Có mô hình nói bất cứ nơi nào mà bạn cần hai lớp cụ thể không?
Anurag

18

Điều đáng chú ý là mô hình i / o Java dựa trên mẫu trang trí. Việc xếp lớp của độc giả này lên trên đầu đọc đó trên đầu ... là một ví dụ thực tế về trang trí.


Có bất kỳ ví dụ nào khác trong các API công khai thực sự không? Đây là người duy nhất tôi biết.
Josiah Yoder

Dường như tất cả các chức năng bao bọc trong tự nhiên đều có một số kiểu trang trí được xây dựng, đó có phải là điều tôi nghĩ không?
Harvey Lin

Ví dụ tốt !!
nagendra547

8

Ví dụ - Kịch bản- Giả sử bạn đang viết một mô-đun mã hóa. Mã hóa này có thể mã hóa tệp rõ ràng bằng cách sử dụng tiêu chuẩn mã hóa dữ liệu DES. Tương tự, trong một hệ thống, bạn có thể có mã hóa dưới dạng AES - Tiêu chuẩn mã hóa nâng cao. Ngoài ra, bạn có thể có sự kết hợp của mã hóa - Đầu tiên là DES, sau đó là AES. Hoặc bạn có thể có AES đầu tiên, sau đó là DES.

Thảo luận- Làm thế nào bạn sẽ phục vụ tình huống này? Bạn không thể tiếp tục tạo đối tượng của các kết hợp đó - ví dụ - AES và DES - tổng cộng 4 kết hợp. Vì vậy, bạn cần phải có 4 đối tượng riêng lẻ Điều này sẽ trở nên phức tạp khi loại mã hóa sẽ tăng lên.

Giải pháp - Tiếp tục xây dựng ngăn xếp - kết hợp tùy theo nhu cầu - trong thời gian chạy. Một ưu điểm khác của phương pháp ngăn xếp này là bạn có thể thư giãn dễ dàng.

Đây là giải pháp - trong C ++.

Đầu tiên, bạn cần một lớp cơ sở - một đơn vị cơ bản của ngăn xếp. Bạn có thể nghĩ như là cơ sở của ngăn xếp. Trong ví dụ này, nó là tập tin rõ ràng. Hãy luôn theo dõi đa hình. Trước tiên hãy tạo một lớp giao diện của đơn vị cơ bản này. Bằng cách này, bạn có thể thực hiện nó như bạn muốn. Ngoài ra, bạn không cần phải nghĩ đến sự phụ thuộc trong khi bao gồm cả đơn vị cơ bản này.

Đây là lớp giao diện -

class IclearData
{
public:

    virtual std::string getData() = 0;
    virtual ~IclearData() = 0;
};

IclearData::~IclearData()
{
    std::cout<<"Destructor called of IclearData"<<std::endl;
}

Bây giờ, thực hiện lớp giao diện này -

class clearData:public IclearData
{
private:

    std::string m_data;

    clearData();

    void setData(std::string data)
        {
            m_data = data;
        }

public:

    std::string getData()
    {
        return m_data;
    }

    clearData(std::string data)
    {
        setData(data);
    }

    ~clearData()
    {
        std::cout<<"Destructor of clear Data Invoked"<<std::endl;
    }

};

Bây giờ, hãy tạo một lớp trừu tượng trang trí - có thể được mở rộng để tạo ra bất kỳ loại hương vị nào - ở đây hương vị là loại mã hóa. Lớp trừu tượng trang trí này có liên quan đến lớp cơ sở. Do đó, trình trang trí "là một" loại giao diện. Vì vậy, bạn cần sử dụng thừa kế.

class encryptionDecorator: public IclearData
{

protected:
    IclearData *p_mclearData;

    encryptionDecorator()
    {
      std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
    }

public:

    std::string getData()
    {
        return p_mclearData->getData();
    }

    encryptionDecorator(IclearData *clearData)
    {
        p_mclearData = clearData;
    }

    virtual std::string showDecryptedData() = 0;

    virtual ~encryptionDecorator() = 0;

};

encryptionDecorator::~encryptionDecorator()
{
    std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}

Bây giờ, hãy tạo một lớp trang trí cụ thể - Loại mã hóa - AES -

const std::string aesEncrypt = "AES Encrypted ";

class aes: public encryptionDecorator
{

private:

    std::string m_aesData;

    aes();

public:

    aes(IclearData *pClearData): m_aesData(aesEncrypt)
    {
        p_mclearData = pClearData;
        m_aesData.append(p_mclearData->getData());
    }

    std::string getData()
        {
            return m_aesData;
        }

    std::string showDecryptedData(void)
    {
        m_aesData.erase(0,m_aesData.length());
        return m_aesData;
    }

};

Bây giờ, giả sử kiểu trang trí là DES -

const std :: string desEncrypt = "Mã hóa DES";

class des: public encryptionDecorator
{

private:

    std::string m_desData;

    des();

public:

    des(IclearData *pClearData): m_desData(desEncrypt)
    {
        p_mclearData = pClearData;
        m_desData.append(p_mclearData->getData());
    }

    std::string getData(void)
        {
            return m_desData;
        }

    std::string showDecryptedData(void)
    {
        m_desData.erase(0,desEncrypt.length());
        return m_desData;
    }

};

Hãy tạo mã khách hàng để sử dụng lớp trang trí này -

int main()
{
    IclearData *pData = new clearData("HELLO_CLEAR_DATA");

    std::cout<<pData->getData()<<std::endl;


    encryptionDecorator *pAesData = new aes(pData);

    std::cout<<pAesData->getData()<<std::endl;

    encryptionDecorator *pDesData = new des(pAesData);

    std::cout<<pDesData->getData()<<std::endl;

    /** unwind the decorator stack ***/
    std::cout<<pDesData->showDecryptedData()<<std::endl;

    delete pDesData;
    delete pAesData;
    delete pData;

    return 0;
}

Bạn sẽ thấy kết quả như sau -

HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData

Đây là sơ đồ UML - Biểu diễn lớp của nó. Trong trường hợp, bạn muốn bỏ qua mã và tập trung vào khía cạnh thiết kế.

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


1
không phải là ví dụ phù hợp hơn cho strategy pattern?
exexzian

@exexzian Vâng, các sinh viên của tôi luôn đề xuất một danh sách các chiến lược cho tôi về loại vấn đề này và nó cũng cảm thấy như một giải pháp sạch nhất đối với tôi.
Josiah Yoder

Không, với mẫu chiến lược bạn không thể kết hợp các phương thức mã hóa. Therofore bạn sẽ phải tạo một lớp chiến lược cho mọi sự kết hợp có thể.
deetz

4

Mẫu trang trí giúp bạn thay đổi hoặc định cấu hình chức năng của đối tượng bằng cách kết nối với các lớp con tương tự khác của đối tượng này.

Ví dụ tốt nhất sẽ là các lớp InputStream và OutputStream trong gói java.io

    File file=new File("target","test.txt");
    FileOutputStream fos=new FileOutputStream(file);
    BufferedOutputStream bos=new BufferedOutputStream(fos);
    ObjectOutputStream oos=new ObjectOutputStream(bos);


    oos.write(5);
    oos.writeBoolean(true);
    oos.writeBytes("decorator pattern was here.");


//... then close the streams of course.

Trong trường hợp này, chuỗi gọi bắt đầu tại ObjectOutputStream, sau đó đi đến lớp Tệp, sau đó lớp Tệp trả về giá trị, sau đó ba lớp con khác thêm tất cả chúng và cuối cùng, giá trị của phương thức ObjectOutputStream trả về nó, là đúng rồi đấy?
Harvey Lin

3

Mẫu thiết kế trang trí trong Java là gì.

Định nghĩa chính thức của mẫu Trang trí từ sách GoF (Mẫu thiết kế: Các yếu tố của phần mềm hướng đối tượng có thể tái sử dụng, 1995, Pearson Education, Inc. Xuất bản với tên Pearson Addison Wesley) cho biết bạn có thể,

"Gắn trách nhiệm bổ sung cho một đối tượng một cách linh hoạt. Các nhà trang trí cung cấp một sự thay thế linh hoạt cho phân lớp để mở rộng chức năng."

Giả sử chúng ta có một chiếc Pizza và chúng ta muốn trang trí nó bằng các loại toppings như Chicken Masala, Onion và Mozzarella Cheese. Hãy xem cách triển khai nó trong Java ...

Chương trình trình bày cách triển khai Mẫu thiết kế trang trí trong Java.

Pizza.java:

<!-- language-all: lang-html -->

package com.hubberspot.designpattern.structural.decorator;

public class Pizza {

public Pizza() {

}

public String description(){
    return "Pizza";
}

}



package com.hubberspot.designpattern.structural.decorator;

public abstract class PizzaToppings extends Pizza {

public abstract String description();

}

package com.hubberspot.designpattern.structural.decorator;

public class ChickenMasala extends PizzaToppings {

private Pizza pizza;

public ChickenMasala(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + " with chicken masala, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class MozzarellaCheese extends PizzaToppings {

private Pizza pizza;

public MozzarellaCheese(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "and mozzarella cheese.";
}
}



package com.hubberspot.designpattern.structural.decorator;

public class Onion extends PizzaToppings {

private Pizza pizza;

public Onion(Pizza pizza) {
    this.pizza = pizza;
}

@Override
public String description() {
    return pizza.description() + "onions, ";
}

}



package com.hubberspot.designpattern.structural.decorator;

public class TestDecorator {

public static void main(String[] args) {

    Pizza pizza = new Pizza();

    pizza = new ChickenMasala(pizza);
    pizza = new Onion(pizza);
    pizza = new MozzarellaCheese(pizza);

    System.out.println("You're getting " + pizza.description());

}

}

3

Tôi đã sử dụng mô hình trang trí rộng rãi trong công việc của tôi. Tôi đã tạo một bài đăng trên blog của mình về cách sử dụng nó với đăng nhập.


Tôi không thích việc bạn vừa ném một liên kết như một câu trả lời. Nhưng bài viết trên Blog của bạn rất hữu ích, tôi chỉ cần upvote :). Bây giờ tôi thực sự hiểu nó. Mọi người đều đến với pizza, và bạn với một ví dụ hoàn hảo.
Niklas Raab

2

Mẫu trang trí cho phép bạn tự động thêm hành vi vào các đối tượng.

Hãy lấy một ví dụ mà bạn cần xây dựng một ứng dụng tính toán giá của các loại bánh mì kẹp thịt khác nhau. Bạn cần xử lý các biến thể khác nhau của bánh mì kẹp thịt, chẳng hạn như "lớn" hoặc "với phô mai", mỗi loại có giá tương đối với bánh mì kẹp thịt cơ bản. Ví dụ: thêm $ 10 cho burger với phô mai, thêm $ 15 cho burger lớn, v.v.

Trong trường hợp này, bạn có thể muốn tạo các lớp con để xử lý chúng. Chúng ta có thể diễn tả điều này trong Ruby như:

class Burger
  def price
    50
  end
end

class BurgerWithCheese < Burger
  def price
    super + 15
  end
end

Trong ví dụ trên, lớp BurgerWithCheese kế thừa từ Burger và ghi đè phương thức giá để thêm $ 15 vào giá được xác định trong siêu hạng. Bạn cũng sẽ tạo một lớp LargeBurger và xác định giá tương đối với Burger. Nhưng bạn cũng cần xác định một lớp mới cho sự kết hợp giữa "lớn" và "với phô mai".

Bây giờ chuyện gì sẽ xảy ra nếu chúng ta cần phục vụ "burger với khoai tây chiên"? Chúng tôi đã có 4 lớp để xử lý các kết hợp đó và chúng tôi sẽ cần thêm 4 lớp nữa để xử lý tất cả kết hợp của 3 thuộc tính - "lớn", "với phô mai" và "với khoai tây chiên". Chúng tôi cần 8 lớp ngay bây giờ. Thêm một tài sản khác và chúng tôi sẽ cần 16. Điều này sẽ tăng lên 2 ^ n.

Thay vào đó, hãy thử xác định một BurgerDecorator có trong một đối tượng Burger:

class BurgerDecorator
  def initialize(burger)
    self.burger = burger
  end
end

class BurgerWithCheese < BurgerDecorator
  def price
    self.burger.price + 15
  end
end

burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price   # => 65

Trong ví dụ trên, chúng tôi đã tạo một lớp BurgerDecorator, từ đó lớp BurgerWithCheese kế thừa. Chúng ta cũng có thể biểu diễn biến thể "lớn" bằng cách tạo lớp LargeBurger. Bây giờ chúng ta có thể định nghĩa một burger lớn với phô mai trong thời gian chạy là:

b = LargeBurger.new(cheese_burger)
b.price  # => 50 + 15 + 20 = 85

Hãy nhớ cách sử dụng tính kế thừa để thêm biến thể "với khoai tây chiên" sẽ liên quan đến việc thêm 4 lớp con nữa? Với các nhà trang trí, chúng tôi sẽ chỉ tạo một lớp mới, BurgerWithFries, để xử lý biến thể mới và xử lý việc này khi chạy. Mỗi tài sản mới sẽ chỉ cần trang trí nhiều hơn để bao gồm tất cả các hoán vị.

Tái bút Đây là phiên bản ngắn của một bài viết tôi đã viết về việc sử dụng Mẫu trang trí trong Ruby , bạn có thể đọc nếu bạn muốn tìm hiểu các ví dụ chi tiết hơn.


2

Người trang trí:

  1. Thêm hành vi cho đối tượng trong thời gian chạy . Kế thừa là chìa khóa để đạt được chức năng này, đó là cả ưu điểm và nhược điểm của mẫu này.
  2. Nó tăng cường hành vi của giao diện.
  3. Trang trí có thể được xem như là một hỗn hợp thoái hóa chỉ với một thành phần. Tuy nhiên, Trình trang trí thêm các trách nhiệm bổ sung - nó không dành cho tổng hợp đối tượng.
  4. Lớp Decorator tuyên bố mối quan hệ thành phần với giao diện LCD (Mẫu số thấp nhất) và thành viên dữ liệu này được khởi tạo trong hàm tạo của nó.
  5. Công cụ trang trí được thiết kế để cho phép bạn thêm trách nhiệm cho các đối tượng mà không cần phân lớp

Tham khảo bài viết chua để biết thêm chi tiết.

Decorator (Trừu tượng) : nó là một lớp / giao diện trừu tượng, thực hiện giao diện thành phần. Nó chứa giao diện Thành phần. Khi không có lớp này, bạn cần nhiều lớp con của ConcreteDecorators cho các kết hợp khác nhau. Thành phần của thành phần làm giảm các lớp con không cần thiết.

Ví dụ về JDK:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
        char c = (char)bis.read();
        System.out.println("Char: "+c);;
}

Hãy xem câu hỏi SE bên dưới để biết sơ đồ UML và ví dụ mã.

Mẫu trang trí cho IO

Bài viết hữu ích:

tạp chí

wikipedia

Ví dụ từ thực của mẫu Trang trí: VendingMachineDecorator đã được giải thích @

Khi nào nên sử dụng mẫu trang trí?

Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();

beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();

Trong ví dụ trên, Trà hoặc Cà phê (Đồ uống) đã được trang trí bởi Sugar và Lemon.


2

Mẫu trang trí đạt được một mục tiêu duy nhất là tự động thêm trách nhiệm cho bất kỳ đối tượng nào .

Mô hình I / O của Java dựa trên mẫu trang trí.

Java IO là mẫu trang trí


1

Có một ví dụ trên Wikipedia về trang trí cửa sổ bằng thanh cuộn:

http://en.wikipedia.org/wiki/Decorator_potype

Dưới đây là một ví dụ rất "thế giới thực" khác về "Thành viên nhóm, trưởng nhóm và người quản lý", minh họa rằng mẫu trang trí không thể thay thế bằng sự kế thừa đơn giản:

https://zishanbilal.wordpress.com/2011/04/11/design-potypes-by-examples-decorator-potype/


Liên kết song phương Zishan đó rất tuyệt - ví dụ hay nhất tôi từng thấy
ném đá

1

Một thời gian trước, tôi đã tái cấu trúc một cơ sở mã bằng cách sử dụng mẫu Decorator, vì vậy tôi sẽ cố gắng giải thích trường hợp sử dụng.

Giả sử chúng ta có một bộ dịch vụ và dựa trên việc người dùng có mua giấy phép của dịch vụ cụ thể hay không, chúng ta cần bắt đầu dịch vụ.

Tất cả các dịch vụ có một giao diện chung

interface Service {
  String serviceId();
  void init() throws Exception;
  void start() throws Exception;
  void stop() throws Exception;
}

Tái cấu trúc

abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId, LicenseManager licenseManager) {
    // assign instance variables
  }

  @Override
  public void init() throws Exception {
    if (!licenseManager.isLicenseValid(serviceId)) {
       throw new Exception("License not valid for service");
    }
    // Service initialization logic
  }
}

Nếu bạn quan sát cẩn thận, ServiceSupportlà phụ thuộc vào LicenseManager. Nhưng tại sao nó phải phụ thuộc vào LicenseManager? Điều gì xảy ra nếu chúng tôi cần dịch vụ nền mà không cần kiểm tra thông tin giấy phép. Trong tình hình hiện tại, chúng tôi sẽ phải bằng cách nào đó đào tạo LicenseManagerđể trở lại truecho các dịch vụ nền. Cách tiếp cận này có vẻ không tốt với tôi. Theo tôi kiểm tra giấy phép và logic khác là trực giao với nhau.

Vì vậy, Mô hình trang trí đến giải cứu và ở đây bắt đầu tái cấu trúc với TDD.

Bài tái cấu trúc

class LicensedService implements Service {
  private Service service;
  public LicensedService(LicenseManager licenseManager, Service service) {
    this.service = service;
  }

  @Override
  public void init() {
    if (!licenseManager.isLicenseValid(service.serviceId())) {
      throw new Exception("License is invalid for service " + service.serviceId());
    }
    // Delegate init to decorated service
    service.init();
  }

  // override other methods according to requirement
}

// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
  public ServiceSupport(String serviceId) {
    // assign variables
  }

  @Override
  public void init() {
    // Service initialization logic
  }
}

// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");

Hành trình

  • Sự gắn kết của mã đã tốt hơn
  • Việc kiểm tra đơn vị trở nên dễ dàng hơn vì không phải giả lập cấp phép khi kiểm tra ServiceSupport
  • Không cần bỏ qua cấp phép bởi bất kỳ kiểm tra đặc biệt nào cho các dịch vụ nền
  • Phân chia trách nhiệm đúng đắn

1

Hãy lấy ví dụ về PubG. Súng trường tấn công hoạt động tốt nhất với zoom 4x và trong khi chúng tôi ở trên đó, chúng tôi cũng sẽ cần bộ bù và bộ triệt. Nó sẽ làm giảm độ giật và giảm âm thanh bắn cũng như tiếng vang. Chúng tôi sẽ cần triển khai tính năng này, nơi chúng tôi sẽ cho phép người chơi mua khẩu súng yêu thích và phụ kiện của họ. Người chơi có thể mua súng hoặc một số phụ kiện hoặc tất cả các phụ kiện và họ sẽ được tính phí theo.

Hãy xem cách trang trí mô hình được áp dụng ở đây:

Giả sử ai đó muốn mua SCAR-L với cả ba phụ kiện được đề cập ở trên.

  1. Lấy một đối tượng của SCAR-L
  2. Trang trí (hoặc thêm) SCAR-L với đối tượng thu phóng 4x
  3. Trang trí SCAR-L với đối tượng triệt tiêu
  4. Trang trí SCAR-L với đối tượng máy nén
  5. Gọi phương thức chi phí và để mỗi đối tượng ủy quyền thêm chi phí bằng phương pháp chi phí phụ kiện

Điều này sẽ dẫn đến một sơ đồ lớp như thế này:

Mẫu trang trí tại nơi làm việc

Bây giờ, chúng ta có thể có các lớp như thế này:

public abstract class Gun {     
    private Double cost;    
    public Double getCost() {           
        return cost;        
       }    
    }

public abstract class GunAccessories extends Gun {  }

public class Scarl extends Gun {    
    public Scarl() {            
        cost = 100;
        }   
     }

public class Suppressor extends GunAccessories {        
    Gun gun;        
    public Suppressor(Gun gun) {            
    cost = 5;           
    this.gun = gun;     
    }               
    public double getCost(){            
        return cost + gun.getCost();
    }
}

public class GunShop{   
    public static void main(String args[]){         
    Gun scarl = new Scarl();                
    scarl = new Supressor(scarl);
    System.out.println("Price is "+scarl.getCost());
    }      
}

Chúng tôi cũng có thể thêm các phụ kiện khác và trang trí cho Gun của mình.

Tài liệu tham khảo:

https://nulpulumexception.com/2019/05/05/a-beginner-guide-to-decorator-potype/


0

Mẫu thiết kế trang trí : Mẫu này giúp sửa đổi các đặc điểm của một đối tượng khi chạy. Nó cung cấp các hương vị khác nhau cho một đối tượng và cho phép linh hoạt lựa chọn thành phần nào chúng ta muốn sử dụng trong hương vị đó.

Ví dụ về cuộc sống thực: Hãy nói rằng bạn có một ghế cabin chính trong một chuyến bay. Bây giờ bạn được phép chọn nhiều tiện nghi với ghế ngồi. Mỗi tiện nghi có chi phí riêng liên quan đến nó. Bây giờ nếu người dùng chọn Wifi và thực phẩm cao cấp, anh ấy / cô ấy sẽ bị tính phí cho ghế + wifi + thực phẩm cao cấp.

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

Trong trường hợp này mẫu thiết kế trang trí thực sự có thể giúp chúng ta. Truy cập liên kết trên để hiểu thêm về mô hình trang trí và thực hiện một ví dụ thực tế.

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.