设计模式——11. 享元模式

1. 说明

享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少系统中相似对象的内存占用或计算开销,通过共享相同的对象来达到节省资源的目的。
享元模式的核心思想是将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State):

  • 内部状态是对象可以共享的部分,它通常存储在享元对象内部,并且不随外部环境的变化而变化。这些状态被多个对象共享。
  • 外部状态是对象的一部分,但它不会被共享,它随着外部环境的变化而变化。

通过将内部状态和外部状态分离,享元模式使得可以共享内部状态的多个对象共享同一个享元对象,从而减少了内存占用和提高了系统性能。

享元模式提供了一种高效地共享对象的方式,从而减少了内存占用和提高了性能,但需要注意的是,使用享元模式会增加系统的复杂性,因为它需要维护共享对象池。

#2. 使用的场景

享元模式在以下情况下可以考虑使用:

  1. 大量相似对象:当一个应用程序需要创建大量相似的对象,而这些对象的区别仅在于它们的外部状态时,可以使用享元模式。通过共享内部状态,可以减少内存消耗,提高性能。
  2. 内部状态和外部状态:如果一个对象的状态可以分为内部状态(可以共享)和外部状态(不可共享),那么可以使用享元模式。内部状态在对象内部管理,而外部状态在对象外部管理,根据需要传递给享元对象。
  3. 缓存:享元模式可以用于实现缓存。例如,在计算密集型应用程序中,可以将计算结果缓存起来,以便在相同输入情况下可以重复使用。
  4. 字形或图标管理:在图形处理软件中,字符、字形或图标通常是相似的,可以使用享元模式来共享它们的共同部分,减少内存占用。
  5. 线程池:线程池可以使用享元模式来管理可重用的线程对象,减少线程的创建和销毁开销。
  6. 游戏开发:在游戏开发中,可以使用享元模式来管理游戏中的角色、道具或粒子效果等。

总之,享元模式适用于需要创建大量相似对象,并且希望通过共享内部状态来减少内存消耗和提高性能的情况。但要注意,使用享元模式会增加一定的复杂性,因为需要维护共享对象池和处理外部状态的传递。因此,应谨慎选择是否使用享元模式。

3. 应用例子

以下是一个使用 Python 实现的简单享元模式示例,模拟了共享电子邮件对象的情况:

# 抽象享元类
class Email:
    def __init__(self, content):
        self.content = content

    def send(self, recipient):
        pass

# 具体享元类
class SharedEmail(Email):
    def send(self, recipient):
        print(f"Shared email sent to {recipient} with content: {self.content}")

# 享元工厂类
class EmailFactory:
    _emails = {}

    @classmethod
    def get_shared_email(cls, content):
        if content not in cls._emails:
            cls._emails[content] = SharedEmail(content)
        return cls._emails[content]

# 客户端
if __name__ == "__main__":
    factory = EmailFactory()

    email1 = factory.get_shared_email("Hello, how are you?")
    email2 = factory.get_shared_email("Please review the document.")
    email3 = factory.get_shared_email("Let's meet tomorrow.")

    email1.send("user1@example.com")
    email2.send("user2@example.com")
    email3.send("user3@example.com")

在这个示例中:

  • Email 是抽象享元类,它包含了一个 send 方法,该方法需要被具体享元类实现。
  • SharedEmail 是具体享元类,它继承了 Email 并实现了 send 方法。这个类表示可以共享的电子邮件对象。
  • EmailFactory 是享元工厂类,负责管理和维护共享的电子邮件对象。它包括一个私有字典 _emails 用于存储已创建的共享电子邮件对象。get_shared_email 方法用于获取或创建共享电子邮件对象。
  • 在客户端代码中,我们使用享元工厂类来获取共享的电子邮件对象,并调用 send 方法发送电子邮件。

这个示例演示了享元模式的概念,其中多个电子邮件对象共享相同的内容,以减少内存占用。

4. 实现要素

享元模式的实现要素包括以下几个部分:

  1. 抽象享元类(Flyweight): 定义了具体享元类的接口,通常包括一个操作方法,该方法的参数中包含外部状态。
  2. 具体享元类(ConcreteFlyweight): 实现抽象享元类的接口,包含内部状态和外部状态。内部状态是可以被多个具体享元对象共享的部分,外部状态是不可共享的部分。
  3. 享元工厂类(FlyweightFactory): 负责管理和维护享元对象,通常包括一个享元对象池。客户端通过工厂类获取具体享元对象,并在需要时传递外部状态。
  4. 客户端(Client): 使用享元对象的客户端,负责维护外部状态,并将外部状态传递给享元对象。

5. Java/golang/javascrip/C++ 等语言实现方式

5.1 Java实现

上述例子用Java语言实现示例如下:

import java.util.HashMap;
import java.util.Map;

// 抽象享元类
interface Email {
    void send(String recipient);
}

// 具体享元类
class SharedEmail implements Email {
    private String content;

    public SharedEmail(String content) {
        this.content = content;
    }

    public void send(String recipient) {
        System.out.println("Shared email sent to " + recipient + " with content: " + content);
    }
}

// 享元工厂类
class EmailFactory {
    private Map<String, Email> emails = new HashMap<>();

    public Email getSharedEmail(String content) {
        if (!emails.containsKey(content)) {
            emails.put(content, new SharedEmail(content));
        }
        return emails.get(content);
    }
}

// 客户端
public class Main {
    public static void main(String[] args) {
        EmailFactory factory = new EmailFactory();

        Email email1 = factory.getSharedEmail("Hello, how are you?");
        Email email2 = factory.getSharedEmail("Please review the document.");
        Email email3 = factory.getSharedEmail("Let's meet tomorrow.");

        email1.send("user1@example.com");
        email2.send("user2@example.com");
        email3.send("user3@example.com");
    }
}

5.2 Golang实现

上述例子用golang实现示例如下:

package main

import "fmt"

// Email 是抽象享元接口
type Email interface {
        Send(recipient string)
}

// SharedEmail 是具体享元类
type SharedEmail struct {
        content string
}

func NewSharedEmail(content string) *SharedEmail {
        return &SharedEmail{content}
}

func (e *SharedEmail) Send(recipient string) {
        fmt.Printf("Shared email sent to %s with content: %s\n", recipient, e.content)
}

// EmailFactory 是享元工厂类
type EmailFactory struct {
        emails map[string]Email
}

func NewEmailFactory() *EmailFactory {
        return &EmailFactory{
                emails: make(map[string]Email),
        }
}

func (f *EmailFactory) GetSharedEmail(content string) Email {
        if email, exists := f.emails[content]; exists {
                return email
        }
        email := NewSharedEmail(content)
        f.emails[content] = email
        return email
}

func main() {
        factory := NewEmailFactory()

        email1 := factory.GetSharedEmail("Hello, how are you?")
        email2 := factory.GetSharedEmail("Please review the document.")
        email3 := factory.GetSharedEmail("Let's meet tomorrow.")

        email1.Send("user1@example.com")
        email2.Send("user2@example.com")
        email3.Send("user3@example.com")
}

5.3 Javascript实现

上述例子用javascript实现示例如下:

// 抽象享元类
class Email {
  constructor(content) {
    this.content = content;
  }

  send(recipient) {}
}

// 具体享元类
class SharedEmail extends Email {
  send(recipient) {
    console.log(`Shared email sent to ${recipient} with content: ${this.content}`);
  }
}

// 享元工厂类
class EmailFactory {
  constructor() {
    this.emails = {};
  }

  getSharedEmail(content) {
    if (!this.emails[content]) {
      this.emails[content] = new SharedEmail(content);
    }
    return this.emails[content];
  }
}

// 客户端
const factory = new EmailFactory();

const email1 = factory.getSharedEmail("Hello, how are you?");
const email2 = factory.getSharedEmail("Please review the document.");
const email3 = factory.getSharedEmail("Let's meet tomorrow.");

email1.send("user1@example.com");
email2.send("user2@example.com");
email3.send("user3@example.com");

5.4 C++实现

上述例子用C++实现如下:

#include <iostream>
#include <map>

// 抽象享元类
class Email {
public:
    virtual void send(const std::string& recipient) = 0;
};

// 具体享元类
class SharedEmail : public Email {
private:
    std::string content;

public:
    SharedEmail(const std::string& content) : content(content) {}

    void send(const std::string& recipient) override {
        std::cout << "Shared email sent to " << recipient << " with content: " << content << std::endl;
    }
};

// 享元工厂类
class EmailFactory {
private:
    std::map<std::string, Email*> emails;

public:
    Email* getSharedEmail(const std::string& content) {
        if (emails.find(content) == emails.end()) {
            emails[content] = new SharedEmail(content);
        }
        return emails[content];
    }

    ~EmailFactory() {
        for (auto& pair : emails) {
            delete pair.second;
        }
        emails.clear();
    }
};

// 客户端
int main() {
    EmailFactory factory;

    Email* email1 = factory.getSharedEmail("Hello, how are you?");
    Email* email2 = factory.getSharedEmail("Please review the document.");
    Email* email3 = factory.getSharedEmail("Let's meet tomorrow.");

    email1->send("user1@example.com");
    email2->send("user2@example.com");
    email3->send("user3@example.com");

    return 0;
}

6. 练习题

假设你正在设计一个图书馆资源管理系统。你需要创建一个 Resource 类来表示不同类型的图书和其他资源。每个资源都有一个唯一的编号、标题和作者。然而,一些资源可能是相同的,例如多本相同的书。在这种情况下,你希望共享相同的资源对象,以节省内存。

要求:

  1. 使用享元模式实现这个图书馆资源管理系统,确保相同的资源在内存中只有一份实例。
  2. 编写一个程序,演示如何创建和管理图书馆资源。

使用 C++、Java、Python 或任何其他编程语言来实现这个练习。
你可以在评论区里或者私信我回复您的答案,这样我或者大家都能帮你解答,期待着你的回复~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guohuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值