Decorator pattern

Trong lập trình hướng đối tượng, decorator pattern (mẫu trang trí) là một mẫu thiết kế cho phép thêm các thao tác mới vào một thao tác có sẵn một cách linh động.

Giới thiệu

[sửa | sửa mã nguồn]

Mẫu trang trí làm việc bằng cách đóng gói đối tượng gốc vào trong một đối tượng "trang trí" mới, thường đạt được bằng cách truyền đối tượng gốc như một tham số tới hàm khởi tạo của decorator, decorator sẽ thực thi các chức năng mới. Giao diện của đối tượng gốc cần được duy trì qua decorator.

Động cơ

[sửa | sửa mã nguồn]
Mô hình UML cho ví dụ về cửa sổ (Window)

Xét một cửa sổ (Window) trong hệ thống giao diện người dùng đồ họa (Graphical User Interface, UI), để cho phép trượt hay cuộn (scroll) trên nội dung của cửa sổ, chúng ta có thể thêm các thanh trượt ngang hoặc thanh trượt dọc vào cửa sổ đó. Giả sử các cửa sổ này được biểu diễn bằng thể hiện của lớp Window, và lớp này không có chức năng thêm thanh trượt. Như vậy, để bổ sung khả năng trượt vào các cửa sổ, chúng ta có thể tạo một lớp con ScrollingWindow cung cấp các chức năng đó, hoặc có thể tạo một lớp ScrollingWindowDecorator đơn thuần thêm chức năng này vào lớp Window có sẵn. Cả hai giải pháp đều được xem là khả thi.

Bây giờ lại giả sử chúng ta muốn thêm các đường biên (border) cho các cửa sổ của chúng ta và một lần nữa, lớp Window gốc không hỗ trợ điều này. Nếu không quan tâm đến việc sửa đổi các thành phần của các lớp đã được tạo, khi này bản thân lớp ScrollingWindow đã tạo ra một kiểu cửa sổ mới, nhưng vẫn như lớp Window gốc là không hỗ trợ tạo các đường biên (chỉ cho phép tạo các thanh trượt). Nếu muốn thêm biên vào tất cả cửa sổ, thông thường chúng ta phải tạo các lớp con WindowWithBorderScrollingWindowWithBorder.

Rõ ràng, hướng giải quyết vấn đề này sẽ ngày càng trở lên tồi tệ với mỗi lần tạo thêm nhiều lớp mới khi thêm chức năng. Một giải pháp khác được đưa ra là sử dụng mẫu decorator, ta chỉ cần tạo một lớp mới BorderedWindowDecorator—trong thời gian thực thi (runtime), như vậy ta có thể trang trí cửa sổ có sẵn với ScrollingWindowDecorator hoặc BorderedWindowDecorator hoặc cả hai nếu thấy thích hợp.

Cấu trúc

[sửa | sửa mã nguồn]

Ví dụ sau sử dụng kịch bản window/scrolling

// the Window interface
interface Window {
    public void draw(); // draws the Window
    public String getDescription(); // returns a description of the Window
}

// implementation of a simple Window without any scrollbars
class SimpleWindow implements Window {
    public void draw() {
        // draw window
    }

    public String getDescription() {
        return "simple window";
    }
}

Các lớp sau chứa các trang trí cho tất cả các lớp Window, bao gồm cả trang trí của chính các lớp đó.

// abstract decorator class - note that it implements Window
abstract class WindowDecorator implements Window {
    protected Window decoratedWindow; // the Window being decorated

    public WindowDecorator(Window decoratedWindow) {
        this.decoratedWindow = decoratedWindow;
    }
}

// the first concrete decorator which adds vertical scrollbar functionaliy
class VerticalScrollBarDecorator extends WindowDecorator {
    public VerticalScrollBarDecorator (Window decoratedWindow) {
        super(decoratedWindow);
    }
 
    public void draw() {
        drawVerticalScrollBar();
        decoratedWindow.draw();
    }

    private void drawVerticalScrollBar() {
        // draw the vertical scrollbar
    }

    public String getDescription() {
        return decoratedWindow.getDescription() + ", including vertical scrollbars";
    }
}

// the second concrete decorator which adds horizontal scrollbar functionaliy
class HorizontalScrollBarDecorator extends WindowDecorator {
    public HorizontalScrollBarDecorator (Window decoratedWindow) {
        super(decoratedWindow);
    }

    public void draw() {
        drawHorizontalScrollBar();
        decoratedWindow.draw();
    }

    private void drawHorizontalScrollBar() {
        // draw the horizontal scrollbar
    }

    public String getDescription() {
        return decoratedWindow.getDescription() + ", including horizontal scrollbars";
    }
}

Đây là chương trình test, nó tạo một mẫu Window sẽ được trang trí đầy đủ (chẳng hạn với các thanh trượt dọc và ngang) và in ra mô tả của nó:

public class DecoratedWindowTest {
    public static void main(String[] args) {
        // create a decorated Window with horizonal and vertical scrollbars
        Window decoratedWindow = new HorizontalScrollBarDecorator(
                new VerticalScrollBarDecorator(new SimpleWindow()));

        // print the Window's description
        System.out.println(decoratedWindow.getDescription());
    }
}

Kết quả in ra của chương trình là ""simple window, including vertical scrollbars, including horizontal scrollbars". Chú ý cách mà hàm getDescription method của hai trang trí trước tiên lấy mô tả của Window đã được trang trí và "trang trí" nó với một hậu tố. Đây là mẫu decorator.

class Car {
    public:
        Car() {};
        virtual float CalculateCost() {};
};

Concreate Component

[sửa | sửa mã nguồn]
class DefaultCar: public Car {
    private: 
        float costOfWheels = 4.0;
        float costOfChassis = 10.5;
        float costOfEngine = 35;
    public:
        float CalculateCost() {
            return costOfWheels + costOfChassis + costOfEngine;
        }
};
class Decorator: public Car {
    protected:
        Car* car;
    public:
        Decorator(Car* paramCar) {
            car = paramCar;
        }
	virtual float CalculateCost() {};
};

Concreate Decorator

[sửa | sửa mã nguồn]
class GPSDecorator: public Decorator {
    private:
        float costOfGpsDevice = 9.3;
    public:
        GPSDecorator(Car* paramCar): Decorator(paramCar){}
        float CalculateCost() {
            return car->CalculateCost() + costOfGpsDevice;
        }
};
class SafeDecorator: public Decorator {
    private:
        float costOfSafeSensors = 15;
    public:
        SafeDecorator(Car* paramCar): Decorator(paramCar){}
        float CalculateCost() {
            return car->CalculateCost() + costOfSafeSensors;
        }
};
class HifiDecorator: public Decorator {
    private:
        float costOfSpeaker = 9;
        float costOfAmpli = 15;
    public:
        HifiDecorator(Car* paramCar): Decorator(paramCar){}
        float CalculateCost() {
            return car->CalculateCost() + costOfSpeaker + costOfAmpli;
        }
};
class AutoDecorator: public Decorator {
    private:
        float costOfAutoProcessor = 20;
    public:
        AutoDecorator(Car* paramCar): Decorator(paramCar){}
        float CalculateCost() {
            return car->CalculateCost() + costOfAutoProcessor;
        }
};
int main() {
   Car* defaultCar = new DefaultCar();
   Car* safeCar = new SafeDecorator(defaultCar);
   Car* GPSAndSafeCar = new SafeDecorator(new GPSDecorator(defaultCar));
   Car* HifiAndGPSAndSafeCar = new HifiDecorator(new SafeDecorator(new GPSDecorator(defaultCar)));
   Car* AutoAndHifiAndGPSAndSafeCar = new AutoDecorator(new HifiDecorator(new SafeDecorator(new GPSDecorator(defaultCar))));

   cout << "Cost of Default car: " << defaultCar->CalculateCost() << "\n";
   cout << "Cost of Safe car: " << safeCar->CalculateCost() << "\n";
   cout << "Cost of GPS and Safe car: " << GPSAndSafeCar->CalculateCost() << "\n";
   cout << "Cost of GPS and Safe and HiFi car: " << HifiAndGPSAndSafeCar->CalculateCost() << "\n";
   cout << "Cost of Auto and GPS and Safe and HiFi car: " << AutoAndHifiAndGPSAndSafeCar->CalculateCost() << "\n";

   return 0;
}

Build with G++

Tham khảo

[sửa | sửa mã nguồn]
Chúng tôi bán
Bài viết liên quan
Lòng lợn – món ăn dân dã liệu có còn được dân yêu?
Lòng lợn – món ăn dân dã liệu có còn được dân yêu?
Từ châu Âu đến châu Á, mỗi quốc gia lại có cách biến tấu riêng với nội tạng động vật, tạo nên một bản sắc ẩm thực đặc trưng
Nhân vật Paimon trong Genshin Impact
Nhân vật Paimon trong Genshin Impact
Paimon là một pé đồng hành siêu dễ thương cùng main chính tham gia phiêu lưu trong thế giới Genshin Impart
JR Pass là gì? Hướng dẫn sử dụng JR Pass đi khắp nước Nhật dễ dàng
JR Pass là gì? Hướng dẫn sử dụng JR Pass đi khắp nước Nhật dễ dàng
Bạn muốn đi nhiều nơi tại Nhật nhưng chi phí đi lại thì quá cao? Hãy yên tâm, lựa chọn của bạn sẽ đơn giản hoá hơn nhiều khi đã có JR Pass là có thể di chuyển khắp mọi miền quê ở đất nước mặt trời mọc
Có thật soi gương diện mạo đẹp hơn 30% so với thực tế?
Có thật soi gương diện mạo đẹp hơn 30% so với thực tế?
Lúc chúng ta soi gương không phải là diện mạo thật và chúng ta trong gương sẽ đẹp hơn chúng ta trong thực tế khoảng 30%