设计模式学习(九)——《大话设计模式》

设计模式学习(九)——《大话设计模式》

装饰模式

装饰模式(Decorator Pattern)是一种设计模式,用于向对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它通过创建一个装饰类包装原有的类,提供更强的扩展功能,这样做既保持了类的核心职责,又允许了向对象动态地添加职责。

工作原理

  • 组件接口(Component):定义了一个对象接口,可以给这些对象动态地添加职责。
  • 具体组件(Concrete Component):是组件接口的一个实现,我们可以给这个对象添加一些职责。
  • 装饰(Decorator):持有一个组件(Component)对象的实例,并定义了与组件接口一致的接口,这样就可以让装饰类在不改变接口的前提下,增加新的职责。
  • 具体装饰(Concrete Decorator):是装饰抽象类的具体实现对象,负责给组件添加新的职责。

应用场景

装饰模式通常用于以下场景:

  • 当需要给一个现有的对象添加额外的功能,而又不希望影响其他对象时。
  • 当需要给对象添加的功能可能在未来会被动态地撤销或添加时。
  • 当扩展一个类的功能比创建子类更灵活时。

优点

  • 装饰模式提供了比继承更有弹性的替代方案。通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。
  • 装饰模式允许对对象进行动态的修改,相比静态继承更加灵活。
  • 可以通过一种透明的方式来扩展对象的功能,用户从外部使用这些功能时不需要了解具体的实现细节。

缺点

  • 使用装饰模式会产生很多小对象,过多地使用装饰模式会使程序变得复杂。
  • 装饰链设置不当可能会导致问题,比如循环引用、过深的装饰链等。

示例代码

# 组件接口
class Coffee:
    def cost(self):
        return 5

# 装饰器基类
class CoffeeDecorator(Coffee):
    def __init__(self, coffee):
        self.coffee = coffee

    def cost(self):
        return self.coffee.cost()

# 具体装饰器
class MilkDecorator(CoffeeDecorator):
    def cost(self):
        return self.coffee.cost() + 2

class SugarDecorator(CoffeeDecorator):
    def cost(self):
        return self.coffee.cost() + 1

# 使用
basicCoffee = Coffee()
milkCoffee = MilkDecorator(basicCoffee)
sugarMilkCoffee = SugarDecorator(milkCoffee)

print("Cost of sugarMilkCoffee:", sugarMilkCoffee.cost())

// 定义一个组件接口,所有消息组件都需要实现这个接口
trait MessageComponent {
    fn display(&self) -> String;
}

// 具体组件:文本消息
struct TextMessage {
    content: String,
}

impl TextMessage {
    // 构造函数,用于创建一个新的文本消息
    fn new(content: &str) -> TextMessage {
        TextMessage {
            content: content.to_string(),
        }
    }
}

impl MessageComponent for TextMessage {
    // 实现 display 方法,返回消息内容
    fn display(&self) -> String {
        self.content.clone()
    }
}

// 装饰器基类,持有一个实现了 MessageComponent 接口的对象
struct MessageDecorator {
    component: Box<dyn MessageComponent>,
}

impl MessageDecorator {
    // 构造函数,接收一个实现了 MessageComponent 接口的对象
    fn new(component: Box<dyn MessageComponent>) -> MessageDecorator {
        MessageDecorator { component }
    }
}

impl MessageComponent for MessageDecorator {
    // 默认实现 display 方法,直接调用内部组件的 display 方法
    fn display(&self) -> String {
        self.component.display()
    }
}

// 具体装饰器:加密装饰器
struct EncryptDecorator {
    component: Box<dyn MessageComponent>,
}

impl EncryptDecorator {
    // 构造函数,接收一个实现了 MessageComponent 接口的对象
    fn new(component: Box<dyn MessageComponent>) -> EncryptDecorator {
        EncryptDecorator { component }
    }

    // 加密方法(示例,未实现真正的加密逻辑)
    fn encrypt(&self, message: &str) -> String {
        format!("加密({})", message)
    }
}

impl MessageComponent for EncryptDecorator {
    // 覆盖 display 方法,添加加密逻辑
    fn display(&self) -> String {
        let message = self.component.display();
        self.encrypt(&message)
    }
}

// 使用示例
fn main() {
    // 创建一个文本消息
    let message = TextMessage::new("Hello, World!");

    // 使用加密装饰器装饰文本消息
    let encrypted_message = EncryptDecorator::new(Box::new(message));

    // 显示加密后的消息
    println!("加密后的消息: {}", encrypted_message.display());
}

代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一个代理或占位符以控制对这个对象的访问。使用代理模式,可以在不改变原始对象接口的情况下,进行一些额外的操作,比如访问控制、延迟初始化、网络请求、记录日志等。代理模式主要分为三种类型:静态代理、动态代理和虚拟代理。

代理模式的组成

  • 主题(Subject)接口:定义了真实对象和代理对象共同的接口,这样就在任何使用真实对象的地方都可以使用代理对象。
  • 真实主题(Real Subject)类:实现了主题接口的类,定义了代理所代表的真实对象。
  • 代理(Proxy)类:也实现了主题接口,用来包含对真实主题的引用,并可以在访问真实主题之前或之后执行一些操作。

代理模式的应用场景

  • 远程代理:为远程对象提供一个本地的代理对象,这样可以隐藏对象位于不同地址空间的事实。
  • 虚拟代理:根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
  • 保护代理:控制对原始对象的访问。保护代理用于对象应该有不同访问权限的时候。
  • 智能引用:指当调用真实的对象时,代理处理另外一些事情,比如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它。

优点与缺点

优点:
  • 可以在不修改被封装对象的代码的情况下进行控制。
  • 可以实现懒加载,对象只有在需要的时候才会被创建。
  • 可以实现访问控制和日志记录等功能。
缺点:
  • 可能会导致系统设计变得复杂,因为引入了很多新类。
  • 请求的响应可能会延迟。

示例代码

from abc import ABC, abstractmethod

class Image(ABC):
    @abstractmethod
    def display(self):
        pass
class RealImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self.load_from_disk()

    def load_from_disk(self):
        print(f"Loading {self.filename}")

    def display(self):
        print(f"Displaying {self.filename}")
class ProxyImage(Image):
    def __init__(self, filename):
        self.filename = filename
        self.real_image = None

    def display(self):
        if self.real_image is None:
            self.real_image = RealImage(self.filename)
        self.real_image.display()
if __name__ == "__main__":
    image1 = ProxyImage("test_10mb.jpg")
    image2 = ProxyImage("test_20mb.jpg")

    # 图像将从磁盘加载
    image1.display()
    print("")

    # 图像不需要从磁盘加载
    image1.display()
    print("")

    # 图像将从磁盘加载
    image2.display()

// 定义图像特征(trait),所有图像类型都需要实现这个特征
trait Image {
    fn display(&self);
}

// 真实图像结构体,表示从磁盘加载的图像
struct RealImage {
    filename: String,
}

impl RealImage {
    // 构造函数,用于创建并加载一个新的真实图像
    fn new(filename: &str) -> RealImage {
        println!("Loading {} from disk.", filename); // 模拟从磁盘加载图像
        RealImage {
            filename: filename.to_string(),
        }
    }
}

// 实现图像特征,允许真实图像被显示
impl Image for RealImage {
    fn display(&self) {
        println!("Displaying {}.", self.filename);
    }
}

// 代理图像结构体,用于控制对真实图像的访问
struct ProxyImage {
    real_image: Option<RealImage>,
    filename: String,
}

impl ProxyImage {
    // 构造函数,用于创建一个新的代理图像,此时不加载真实图像
    fn new(filename: &str) -> ProxyImage {
        ProxyImage {
            real_image: None,
            filename: filename.to_string(),
        }
    }
}

// 实现图像特征,允许通过代理来显示图像
impl Image for ProxyImage {
    fn display(&self) {
        match &self.real_image {
            Some(real_image) => {
                // 如果真实图像已加载,则直接显示
                real_image.display();
            },
            None => {
                // 如果真实图像未加载,则先加载再显示
                let real_image = RealImage::new(&self.filename);
                real_image.display();
            },
        }
    }
}

fn main() {
    let image1 = ProxyImage::new("test_10mb.jpg");
    let image2 = ProxyImage::new("test_20mb.jpg");

    // 第一次显示时,将从磁盘加载图像
    image1.display();
    println!("");

    // 第二次显示时,由于真实图像已加载,将直接显示而无需再次加载
    image1.display();
    println!("");

    // 对于另一个图像对象,如果尝试显示,也将经历相同的加载过程
    image2.display();
}

  • 47
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值