前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【愚公系列】2023年11月 二十三种设计模式(十一)-享元模式(Flyweight Pattern)

【愚公系列】2023年11月 二十三种设计模式(十一)-享元模式(Flyweight Pattern)

原创
作者头像
愚公搬代码
发布2023-11-12 20:44:21
1920
发布2023-11-12 20:44:21
举报
文章被收录于专栏:历史专栏历史专栏

? 作者简介,愚公搬代码 ?《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。 ?《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。

?《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。

??欢迎 ?点赞?评论?收藏

?前言

设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。

毫无疑问,设计模式对个人、团队和整个系统都带来了显著的益处。它们将代码开发提升到工程化水平,为软件工程构建提供了坚实的基础,就如同大厦的一块块精巧的砖石一样。在项目中明智地应用设计模式可以完美地解决各种复杂问题。每种设计模式都有相应的原理和最佳实践,它们描述了我们日常开发中不断遇到的问题,以及这些问题的核心解决方法。正是因为这种实用性和通用性,设计模式才能在软件开发中广泛地得以应用。设计模式是构建稳健、可扩展和可维护软件的关键工具,为开发者们提供了解决问题的智慧和指导。

?一、享元模式(Flyweight Pattern)

享元模式属于结构型设计模式,其主要目标是通过共享已存在的对象,高效地支持大量细粒度对象的创建和管理。该模式通过减少对象实例的创建,降低了系统的性能开销。

在享元模式中,系统会尝试重用已存在的相似对象。如果找到匹配的对象,它将直接返回该对象;如果未找到匹配的对象,则会创建新的对象。这种机制有助于减少内存占用和提高系统性能,特别是在需要大量相似对象的情况下。

简而言之,享元模式通过共享和重用对象,有效地优化了对象创建和管理的性能,提高了系统的效率。

?二、使用步骤

?1.角色

?1.1 抽象享元(Flyweight)

享元模式(Flyweight Pattern)中的抽象享元(Flyweight)是该模式的核心概念之一,它具有重要的作用。以下是抽象享元的概念和作用:

  1. 概念:抽象享元是享元模式中的一个抽象类或接口,定义了享元对象的通用接口,包括可以共享的状态和非共享的状态。通常,抽象享元中包含了用于获取或操作状态的方法。
  2. 作用
    • 定义共享接口:抽象享元定义了享元对象的接口,确保具体享元类实现了必要的方法。这样,所有的具体享元都可以通过统一的接口进行访问和操作。
    • 管理共享状态:抽象享元通常包括一些用于管理共享状态的方法。这些方法允许具体享元对象获取、设置或操作共享的状态信息。
    • 确保共享性:抽象享元通过强制共享状态的使用,确保了享元对象能够被多个客户端共享,以降低内存开销。
    • 定义非共享状态:虽然抽象享元主要关注共享状态,但它也可以包含一些非共享状态的方法。这些方法通常在具体享元中实现,用于处理与特定客户端相关的信息。

抽象享元在享元模式中起着重要的角色,它定义了享元对象的通用接口和共享状态的管理方式,确保了对象的共享性和高效使用,同时也允许具体享元类添加特定于客户端的信息。这有助于降低内存消耗,提高系统的性能。

?1.2 具体享元(Concrete Flyweight)

在享元模式(Flyweight Pattern)中,具体享元(Concrete Flyweight)是一种享元对象的具体实现,它实现了抽象享元接口(Abstract Flyweight),并包含了一部分内部状态。以下是具体享元的概念和作用:

  1. 概念:具体享元是享元模式中的一种对象类型,它表示可以被共享和重用的具体对象。每个具体享元对象都实现了抽象享元接口中定义的方法,并可以包含一些内部状态,其中一部分状态是可以被共享的。
  2. 作用
    • 实现共享接口:具体享元对象必须实现抽象享元接口中定义的方法,以确保能够被客户端访问和共享。这包括实现获取和操作共享状态的方法。
    • 共享状态:具体享元对象通常包含一些共享状态,这些状态可以被多个具体享元对象共享。这些共享状态存储在享元对象内部,以降低内存占用。
    • 非共享状态:除了共享状态外,具体享元对象也可以包含一些非共享状态,这些状态是特定于对象的,不会被共享。客户端可以在创建享元对象时传递非共享状态信息。
    • 提高性能:具体享元的主要作用是提高系统性能。通过共享具体享元对象,可以减少内存消耗和对象创建的开销,从而提高系统的效率。

具体享元的创建和管理通常由享元工厂(Flyweight Factory)来完成。享元工厂负责创建具体享元对象,并在需要时共享已存在的对象,以确保对象的重用。具体享元对象在多次请求中被共享,以降低资源消耗,特别适用于需要大量相似对象的情况。

具体享元是享元模式的具体实现,它允许多个对象共享相同的状态信息,以降低内存消耗和提高性能。通过将共享状态与非共享状态分离,具体享元对象能够在多个客户端之间共享,并在需要时动态创建。这使得享元模式在某些情况下能够有效地优化应用程序的资源利用率

?1.3 享元工厂(FlyweightFactoiy)

享元模式(Flyweight Pattern)中的享元工厂(Flyweight Factory)是一个关键组件,负责创建和管理享元对象,以确保它们被有效地共享和重用。以下是享元工厂的概念和作用:

  1. 概念:享元工厂是享元模式的一部分,它是用于创建和管理享元对象的类。它维护了一个享元对象的池(或缓存),在池中存储已经创建的享元对象,以便在需要时能够共享已存在的对象,而不是重复创建新对象。
  2. 作用
    • 对象创建:享元工厂负责创建享元对象的实例。当客户端请求创建一个享元对象时,工厂会检查池中是否已存在相应的对象。
    • 对象共享:如果池中已经存在相应的享元对象,享元工厂将直接返回该对象的引用,而不是创建新的对象。这确保了相似对象的共享。
    • 对象管理:享元工厂负责维护享元对象的池,包括添加新对象、查找现有对象和删除对象等操作。
    • 提高性能:享元工厂的主要目标是提高系统性能,通过共享对象减少内存占用和对象创建的开销,从而提高系统的效率。
    • 支持非共享状态:在某些情况下,享元工厂也可以支持传递客户端特定的非共享状态信息给享元对象,以完善对象的状态。
  3. 池管理:享元工厂需要实现合适的数据结构来管理对象池,通常使用哈希表或其他适合快速查找的数据结构。这样可以快速检索已存在的对象。
  4. 适用场景:享元工厂适用于需要大量相似对象,并且希望通过共享来减少内存消耗和提高性能的情况。它在多次创建和销毁对象的场景中特别有用。

享元工厂在享元模式中扮演着关键的角色,它管理着享元对象的创建、共享和池化,以提高系统的效率和性能。通过享元工厂,可以有效地重用已存在的对象,降低内存开销,适用于需要大量相似对象的情况。

?2.示例

在这里插入图片描述
在这里插入图片描述

命名空间FlyweightPattern中包含IConnection接口充当抽象享元,Connection类充当具体享元,ConnectionFactory工厂类充当享元工厂。本案例通过使用享元模式来共享数据库连接。

代码语言:c#
复制
public interface IConnection {

    void Print();

}

IConnection接口,包含一个打印的方法。

代码语言:c#
复制
public class Connection : IConnection {
 
    private string _connectionString = null;
 
    public Connection(string connectionString) {
        _connectionString = connectionString;
        Thread.Sleep(1000);
        Console.WriteLine("It took 1 second(s) to create a connection!");
    }
 
    public void Print() {
        Console.WriteLine($"Database connection is {_connectionString}");
        Console.WriteLine("-------------------------------------------------------");
    }
 
}

Connection类,定义数据库连接(演示)。

代码语言:c#
复制
public class ConnectionFactory {

    private Dictionary<string, IConnection> _connections = null;

    private string _connectionString = null;

    public ConnectionFactory() {
        _connections = new Dictionary<string, IConnection>();
    }

    public IConnection CreateConnection(string connectionString) {
        if (!_connections.ContainsKey(connectionString)) {
            Console.WriteLine("Creating a new connection!");
            IConnection connection = new Connection(connectionString);
            _connections.Add(connectionString, connection);
            return connection;
        }
        else {
            Console.WriteLine("Return an exist connection!");
            var connection = _connections[connectionString] as IConnection;
            return connection;
        }
    }

}

ConnectionFactory类,数据库连接工厂,内部维持对所有连接的引用,CreateConnection方法在发现连接存在时直接返回,如果不存在,则创建一个新的连接并维持进列表。

注:实际开发过程中应该用HashCode来检索数据库连接是否存在。

代码语言:c#
复制
public class Program {

    private static ConnectionFactory _factory = null;

    private static List<string> _connections = null;

    private static IConnection _connection = null;

    private static void Print(int index) {
        if (index > _connections.Count - 1) {
            Console.WriteLine("Index Out Of Range Exception!");
            return;
        }
        _connection = _factory.CreateConnection(_connections[index]);
        _connection.Print();
    }

    public static void Main(string[] args) {
        _connections = new List<string> {
            "Server=Aron1;Database=pubs;\n" + "Uid=uid;Pwd=password;",
            "Provider=sqloledb;Data Source=Aron1;\n" + "User Id=uid;Password=password;",
            "Data Source=192.168.0.1,1433;\n" + "UserID=uid;Password=password;"
        };

        _factory = new ConnectionFactory();

        Print(0);
        Print(1);
        Print(2);
        Print(1);
        Print(3);

        Console.ReadKey();
    }

}

以上是调用方的代码,以下是这个案例的输出结果:

代码语言:c#
复制
Creating a new connection!
It took 1 second(s) to create a connection!
Database connection is Server=Aron1;Database=pubs;
Uid=uid;Pwd=password;
-------------------------------------------------------
Creating a new connection!
It took 1 second(s) to create a connection!
Database connection is Provider=sqloledb;Data Source=Aron1;
User Id=uid;Password=password;
-------------------------------------------------------
Creating a new connection!
It took 1 second(s) to create a connection!
Database connection is Data Source=192.168.0.1,1433;
UserID=uid;Password=password;
-------------------------------------------------------
Return an exist connection!
Database connection is Provider=sqloledb;Data Source=Aron1;
User Id=uid;Password=password;
-------------------------------------------------------
Index Out Of Range Exception!

?总结

?1.优点

享元模式(Flyweight Pattern)具有多个优点,特别适用于需要大量对象的情况,其中许多对象具有相似的属性和行为。以下是享元模式的主要优点:

  1. 减少内存消耗:享元模式通过共享对象来降低内存消耗。相似对象的共享状态被存储在享元对象内部,而不是每个对象都独立存储,从而大幅减少了内存使用。
  2. 提高性能:由于对象共享和重用,享元模式可以显著提高系统性能。减少对象的创建和销毁开销,使系统更加高效。
  3. 支持大量对象:享元模式适用于需要管理大量对象的情况,而不会因为对象过多而导致内存不足或性能下降。
  4. 分离共享状态和非共享状态:享元模式明确将对象的共享状态和非共享状态分离开来。这使得共享状态可以在多个对象之间共享,而非共享状态可以根据需要进行定制。
  5. 支持多线程环境:在多线程环境中,享元模式可以安全地共享对象,避免了线程竞争和同步的问题,提高了并发性能。
  6. 提高代码可维护性:通过将共享状态集中管理,享元模式使代码更加清晰和可维护。共享状态的变化只需在一个地方进行维护,而不需要修改大量对象的状态。
  7. 节省资源:由于对象共享,系统中创建的对象数量较少,因此节省了资源,包括CPU时间和内存。
  8. 可定制性:享元模式允许客户端在创建享元对象时传递非共享状态信息,从而允许客户端根据需要进行个性化定制。
  9. 提高对象创建速度:由于享元模式共享已存在的对象,对象创建的速度更快,因为不需要重新构建对象,而是直接从享元工厂获取。

享元模式通过共享对象的方式来优化内存利用和提高性能,特别适用于需要管理大量相似对象的情况。这种模式在游戏开发、图形处理、文本编辑器等领域有广泛的应用。

?2.缺点

享元模式(Flyweight Pattern)具有一些缺点,需要在使用时考虑。以下是享元模式的一些缺点:

  1. 复杂性增加:引入享元模式可能会增加代码的复杂性。需要创建享元对象和享元工厂来管理对象的共享和创建,这可能会增加代码的复杂性和理解难度。
  2. 共享状态限制:享元模式适用于能够共享状态的对象。如果对象之间的差异较大,无法共享足够的状态信息,那么享元模式可能不适用。
  3. 状态外部化:在享元模式中,共享的状态通常会被外部化,存储在享元对象之外。这可能导致一些信息分散在不同的地方,增加了代码的复杂性和维护难度。
  4. 维护共享状态一致性:共享状态的一致性需要得到维护。如果多个享元对象共享相同的状态,当其中一个对象修改了状态时,可能会影响到其他对象。需要确保状态的修改不会破坏其他对象的正确性。
  5. 非共享状态管理:虽然享元模式支持非共享状态,但管理这些非共享状态可能会变得复杂,特别是当非共享状态的种类和数量较多时。
  6. 适用性限制:享元模式适用于特定的场景,主要是针对需要大量对象且对象之间有重复状态的情况。在某些情况下,引入享元模式可能会过于复杂,不划算。
  7. 性能开销:虽然享元模式可以提高性能,但在某些情况下,由于对象池的维护和状态共享的处理,可能会引入一些额外的性能开销。
  8. 增加代码可读性难度:如果不谨慎使用享元模式,可能会使代码变得难以理解,因为共享状态和非共享状态可能会分散在多个地方,不易于跟踪和维护。

享元模式在适当的情况下可以提供明显的性能和内存优势,但需要仔细权衡其优点和缺点,确保它适合特定的应用场景。在某些情况下,使用享元模式可能会增加复杂性,而在其他情况下,它可以有效地优化资源利用。

?3.使用场景

享元模式(Flyweight Pattern)适用于以下情况和场景:

  1. 大量对象:当应用程序需要创建大量相似的对象时,享元模式可以有效减少内存消耗。例如,文本编辑器中的字符或图像处理中的像素对象。
  2. 共享相同状态:享元模式适用于对象之间存在相同或可共享的状态信息。这些状态信息可以被多个对象共享,从而减少内存占用。例如,多个按钮对象具有相同的样式和行为。
  3. 状态外部化:当对象的状态可以被外部化,并且可以在多个对象之间共享时,可以考虑使用享元模式。共享的状态信息通常存储在享元对象之外,可以在多个对象之间共享。
  4. 性能优化:在需要优化性能的应用中,享元模式可以减少对象的创建和销毁开销,从而提高系统的性能。例如,游戏中的粒子系统可以使用享元模式来提高性能。
  5. 多线程环境:在多线程环境中,享元模式可以确保多个线程安全地共享对象,避免竞态条件和同步问题。
  6. 文本和图形处理:在文本编辑器、图像处理软件和绘图应用程序中,可以使用享元模式来管理字符、像素、图形元素等对象,以降低内存消耗。
  7. 缓存管理:享元模式常用于缓存管理中,以缓存已创建的对象,提高数据检索和访问的速度。例如,Web应用中的页面缓存或数据库查询结果缓存。
  8. 游戏开发:在游戏开发中,大量的游戏对象(如敌人、子弹、道具等)可能具有相似的属性和行为,享元模式可以用于管理和复用这些对象,提高游戏性能。

享元模式在需要管理大量相似对象、共享状态信息、提高性能和降低内存消耗的情况下非常有用。它可以在多种领域中发挥作用,但需要谨慎设计和实现,以确保正确地管理共享状态和非共享状态。


我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ?前言
  • ?一、享元模式(Flyweight Pattern)
  • ?二、使用步骤
    • ?1.角色
      • ?1.1 抽象享元(Flyweight)
      • ?1.2 具体享元(Concrete Flyweight)
      • ?1.3 享元工厂(FlyweightFactoiy)
    • ?2.示例
    • ?总结
      • ?1.优点
        • ?2.缺点
          • ?3.使用场景
          相关产品与服务
          云开发 CloudBase
          云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
          http://www.vxiaotou.com