设计模式(十二)—— 享元模式

模式简介


运用共享技术有效地支持大量细粒度地对象。

通常情况下,面向对象技术可以增强系统地灵活性及可扩展性,在系统开发过程中,我们会不断地增加类和对象。当对象数量过多时,将会带来系统开销过高、性能下降等问题。享元模式通过共享相同或相似的对象来解决这一类问题。在介绍享元模式之前,首先要弄清楚两个概念:内部状态(Intrinsic State)外部状态(Extrinsic State)

  • 内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。例如,围棋中的棋子,它们的形状和大小完全相同,那么形状、大小则属于内部状态。
  • 外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。例如,棋子的位置各不相同,那么棋子的位置就属于外部状态。

结构说明



角色说明

  • Flyweight

抽象享元类。包含一个方法,通过这个方法flyweight可以接受外部状态。

  • ConcreteFlyweight

具体享元类。实现Flyweight,对象是可共享的。

  • UnsharedConcreteFlyweight

非共享享元类。享元模式并不强制共享。

  • FlyweightFactory

享元工厂类。创建并管理Flyweight对象。

源码结构

抽象享元类Flyweight,包含一个抽象方法Operation,并接受一个外部状态extrinsiState。

abstract class Flyweight
{
    public abstract void Operation(int extrinsicState);
}

具体享元类ConcreteFlyweightA和ConcreteFlyweightB,实现具体的Operation方法。

class ConcreteFlyweightA : Flyweight
{
    public override void Operation(int extrinsicState)
    {
        Console.WriteLine($"ConcreteFlyweightA->Operation[{extrinsicState}]");
    }
}

class ConcreteFlyweightB : Flyweight
{
    public override void Operation(int extrinsicState)
    {
        Console.WriteLine($"ConcreteFlyweightB->Operation[{extrinsicState}]");
    }
}

享元工厂,负责维护一个享元池,用来存储具有相同内部状态的享元对象。

class FlyweightFactory
{
    private Dictionary<string, Flyweight> _flyweights = new Dictionary<string, Flyweight>();
    public Flyweight GetFlyweight(string key)
    {
        Flyweight flyweight;
        if (_flyweights.ContainsKey(key))
        {
            Console.WriteLine("Already in the pool , use the exist one.");
            flyweight = _flyweights[key];
        }
        else
        {
            switch (key)
            {
                case "A":
                    flyweight = new ConcreteFlyweightA();
                    break;
                case "B":
                    flyweight = new ConcreteFlyweightB();
                    break;
                default:
                    throw new Exception("Don‘t support this key");
            }

            _flyweights.Add(key, flyweight);
        }
        return _flyweights[key];
    }
}

通过向工厂传入不同的key值,获取相应的享元对象。

class Program
{
    static void Main(string[] args)
    {
        FlyweightFactory factory = new FlyweightFactory();
        Flyweight fw = factory.GetFlyweight("A");

        int extrinsicState = 1;
        fw.Operation(extrinsicState);

        Flyweight fw2 = factory.GetFlyweight("B");
        fw2.Operation(extrinsicState);

        extrinsicState = 2;
        Flyweight fw3 = factory.GetFlyweight("A");
        fw3.Operation(extrinsicState);

        Console.ReadLine();
    }
}

输出结果:

工作原理

  • ConcreteFlyweight对象存储内部状态,由Client计算或存储外部状态并传递给享元对象。
  • 用户通过FlyweightFactory对象获取ConcreteFlyweight对象,从而确保享元对象的共享。

示例分析



想象一款围棋游戏,棋盘中包含大量的黑子和白子。当然我们不能为每个棋子创建一个对象,这种设计代价太大,随着游戏的不断进行,软件占用内存会越来越大。棋子的形状、大小完全相同(内部状态),位置发生变化(外部状态)。

首先创建享元类Chessman,包含抽象方法Display,这里以x,y表示坐标位置。

abstract class Chessman
{
    public abstract void Display(int x, int y);
}

创建具体享元类,为了使用方便,加入Color枚举。

class WhiteChessman : Chessman
{
    public override void Display(int x, int y)
    {
        Console.WriteLine($"While Chessman , Position:X=>{x},Y=>{y}");
    }
}

class BlackChessman : Chessman
{
    public override void Display(int x, int y)
    {
        Console.WriteLine($"Black Chessman , Position:X=>{x},Y=>{y}");
    }
}

public enum Color
{
    White,
    Black
}

创建Chessman工厂,维护一个享元池,如果享元池中包含请求的对象,则直接返回;如果不包含,则根据请求的颜色创建相应的对象,并添加至享元池中,最后返回给客户端。

class ChessmanFactory
{
    private Dictionary<Color, Chessman> _chessman = new Dictionary<Color, Chessman>();
    public Chessman GetChessman(Color color)
    {
        Chessman chessman;
        if (_chessman.ContainsKey(color))
        {
            Console.WriteLine("Already in the pool , use the exist one.");
            chessman = _chessman[color];
        }
        else
        {
            Console.WriteLine("new object");
            switch (color)
            {
                case Color.White:
                    chessman = new WhiteChessman();
                    _chessman.Add(color,chessman);
                    break;
                case Color.Black:
                    chessman = new BlackChessman();
                    _chessman.Add(color, chessman);
                    break;
                default:
                    throw new Exception("Don‘t support this color");
            }
        }

        return chessman;
    }
}

客户端调用

class Program
{
    static void Main(string[] args)
    {
        ChessmanFactory factory = new ChessmanFactory();
        var chessmanA = factory.GetChessman(Color.White);
        chessmanA.Display(1,1);

        var chessmanB = factory.GetChessman(Color.Black);
        chessmanB.Display(4, 3);

        var chessmanC = factory.GetChessman(Color.White);
        chessmanC.Display(5, 10);

        Console.ReadLine();
    }
}

使用场景


  • 一个系统中含有大量的相似或相同的对象,由于这些对象的大量使用,消耗大量的内存。
  • 对象的大部分状态可以外部化,由客户端将这些外部状态传入对象中。
  • 由于享元模式需要维护一个存储享元对象的享元池,所以仅当在多次重复使用享元对象时才值得使用享元模式。

优缺点


优点

  • 极大地减少了内存中对象的数量,使得相同或相似的对象在内存中只保留一份。
  • 享元模式的外部状态相对独立,不会影响其内部状态,从而使得享元对象可以在不同环境中被共享。

缺点

  • 系统逻辑更加复杂,需要分离出外部状态和内部状态。
  • 客户端计算并存储外部状态,导致系统运行时间变长。从某种程度上来说,享元模式是一种以时间换空间的解决方案。

原文地址:https://www.cnblogs.com/Answer-Geng/p/9173513.html

时间: 2024-10-08 03:34:54

设计模式(十二)—— 享元模式的相关文章

设计模式 ( 十二 ) 职责链模式(Chain of Responsibility)(对象行为)

 设计模式(十二)职责链模式(Chain of Responsibility)(对象行为型) 1.概述 你去政府部门求人办事过吗?有时候你会遇到过官员踢球推责,你的问题在我这里能解决就解决.不能解决就推卸给另外个一个部门(对象).至于究竟谁来解决问题呢?政府部门就是为了能够避免屁民的请求与官员之间耦合在一起,让多个(部门)对象都有可能接收请求,将这些(部门)对象连接成一条链,而且沿着这条链传递请求.直到有(部门)对象处理它为止. 样例1:js的事件浮升机制 样例2: 2.问题 假设有多个对象都有

设计模式(十)享元模式Flyweight(结构型)

说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面向对象技术可以很好地解决系统一些灵活性或可扩展性或抽象性的问题,但在很多情况下需要在系统中增加类和对象的个数.当对象数量太多时,将导致运行代价过高,带来性能下降等问题.比如:例子1:图形应用中的图元等对象.字处理应用中的字符对象等. 2.解决方案: 享元模式(Flyweight):对象结构型模式运用

设计模式(十)享元模式(Flyweight)-结构型

享元模式Flyweight 组合模式解决了对象时树形结构时的处理方式.当系统需要大量使用重复的对象,而这些对象要消耗很大的资源时就需要使用享元模式来解决. 单例模式,一个类只有一个唯一的对象.也就是说,不管new多少次,只需要创建这个类的一个对象,如果不采用单例模式,没new一次就会创建一个对象,这对于系统需要使用大量重复的对象,而这些对象需要消耗很大的资源时,是很不划算的,这时就需要使用享元模式. 数据库连接池就是享元模式的典型应用. 享元模式实现原理 享元模式的实现原理图 享元模式的优缺点

java设计模式 GOF23 11 享元模式

一.享元模式简介 如果有很多相同或者相似的对象可以使用享元模式,从而节约内存. 二.关键点 享元对象需要区分内部状态和外部状态. 内部状态:可以共享,不会随着外部状态改变. 外部状态:不可以共享,随外部状态改变. 享元共享类实现享元池管理享元对象. 三.简单实现 package com.lz.flyWeight; /* * 享元模式 * 内部状态 * 模拟棋盘,因为一盘棋其中棋子的颜色都一样 * 只有位置不一样,所以可以将颜色共享,这样可以节省内存空间 * 棋盘接口 */ interface C

大话设计模式Python实现- 享元模式

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象. 下面是一个享元模式的demo: 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 __author__ = 'Andy' 5 """ 6 大话设计模式 7 设计模式--享元模式 8 享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度的对象 9 对一个类进行的实例,只在第一次使用时建立,其他时候是用同一个

Java描述设计模式(18):享元模式

本文源码:GitHub·点这里 || GitEE·点这里 一.使用场景 应用代码 public class C01_InScene { public static void main(String[] args) { String c0 = "cicada" ; String c1 = "cicada" ; System.out.println(c0 == c1); // true } } String类型就是使用享元模式.String对象是final类型,对象一旦

Java设计模式之《享元模式》及应用场景

原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6542449.html 享元模式:"享"就是分享之意,指一物被众人共享,而这也正是该模式的终旨所在. 享元模式有点类似于单例模式,都是只生成一个对象来被共享使用.这里有个问题,那就是对共享对象的修改,为了避免出现这种情况,我们将这些对象的公共部分,或者说是不变化的部分抽取出来形成一个对象.这个对象就可以避免到修改的问题. 享元的目的是为了减少不会要额内存消耗,将多个对同一对象的访

设计模式--13、享元模式

享元模式: 先让我们来看一个应用场景: 比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象.如果有一个1M的文本,那么字母是何其的多,如果每个字母都定义一个对象那么内存早就爆了.那么如果要是每个字母都共享一个对象,那么就大大节约了资源.也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象. 在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式.Flyw

[设计模式] javascript 之 享元模式;

享元模式说明 定义:用于解决一个系统大量细粒度对象的共享问题: 关健词:分离跟共享: 说明: 享元模式分单纯(共享)享元模式,以及组合(不共享)享元模式,有共享跟不共享之分:单纯享元模式,只包含共享的状态,可共享状态是不可变,不可修改的,这是享元的内部状态:当然有外部状态就有外部状态,外部状态是可变的,不被共享,这个外部状态由客户端来管理,是可变化的:外部状态与内部状态是独立分开的,外部状态一般作为参数传入享元对象内,但不会影响内部状态的值:外部状态,一用用于获取共享的享元对象,或多或少与内部状

《Java设计模式》之享元模式

Flyweight在拳击比赛中指最轻量级,即"蝇量级"或"雨量级",这里选择使用"享元模式"的意译,是因为这样更能反映模式的用意.享元模式是对象的结构模式.享元模式以共享的方式高效地支持大量的细粒度对象. Java中的String类型 在JAVA语言中,String类型就是使用了享元模式.String对象是final类型,对象一旦创建就不可改变.在JAVA中字符串常量都是存在常量池中的,JAVA会确保一个字符串常量在常量池中只有一个拷贝.Stri