设计模式之-享元模式,快速掌握享元模式,通俗易懂的讲解享元模式以及它的使用场景

2023-12-26 11:23:15


一、快速理解享元模式

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来最大限度地减少内存使用和提高性能。它适用于需要创建大量相似对象的情况,通过共享相同的状态来减少对象的数量,从而节省内存和提高系统的效率。

通俗易懂的解释: 想象一下,你在一个游戏中扮演军队指挥官,需要管理成千上万的士兵。每个士兵都有自己的外观、武器等属性,如果每个士兵都创建一个独立的对象,将会消耗大量的内存。而享元模式就像是在军队里共享相同外观的士兵,节省了内存空间。

二、使用场景

享元模式适用于以下情况:

  1. 系统中存在大量相似对象,并且这些对象可以共享相同的状态。
  2. 对象的大部分状态可以外部化,并且可以通过参数传递给对象。

三、优缺点

优点:

  1. 减少内存消耗:通过共享相同状态的对象,减少了需要创建的对象数量,从而节省了内存空间。
  2. 提高性能:由于减少了对象数量,可以减少对内存的频繁操作,提高系统的性能。
  3. 增加对象复用性:通过共享对象,可以在不同的上下文中重复使用对象,提高了对象的复用性。

缺点:

  1. 共享对象的状态是不可变的:由于多个对象共享相同的状态,如果一个对象的状态发生改变,可能会影响到其他对象,因此共享的状态应该是不可变的。
  2. 对象共享可能导致线程安全问题:如果多个线程同时访问共享对象并修改其状态,可能会导致线程安全问题,需要进行适当的同步处理。

四、示例代码

下面是一个简单的示例代码来说明享元模式的应用:

假设我们要创建一个文字处理器,需要处理大量的文本字符。为了节省内存,我们希望共享相同字符的对象。

首先,我们定义一个接口 TextCharacter,表示文本字符:

public interface TextCharacter {
    void display();
}

然后,我们实现具体的文本字符类 ConcreteCharacter,包含一个字符属性 character:

public class ConcreteCharacter implements TextCharacter {
    private char character;

    public ConcreteCharacter(char character) {
        this.character = character;
    }

    @Override
    public void display() {
        System.out.println("Character: " + character);
    }
}

接下来,我们创建一个工厂类 CharacterFactory,用于管理和共享文本字符对象。它包含一个字符对象池 characterPool,使用 HashMap 来存储共享的字符对象:

public class CharacterFactory {
    private Map<Character, TextCharacter> characterPool;

    public CharacterFactory() {
        characterPool = new HashMap<>();
    }

    public TextCharacter getCharacter(char character) {
        TextCharacter textCharacter = characterPool.get(character);

        if (textCharacter == null) {
            textCharacter = new ConcreteCharacter(character);
            characterPool.put(character, textCharacter);
        }

        return textCharacter;
    }
}

最后,我们可以使用享元模式来创建和展示文本字符:

public class TextProcessor {
    public static void main(String[] args) {
        CharacterFactory characterFactory = new CharacterFactory();

        TextCharacter charA = characterFactory.getCharacter('A');
        charA.display();

        TextCharacter charB = characterFactory.getCharacter('B');
        charB.display();

        TextCharacter charA2 = characterFactory.getCharacter('A');
        charA2.display();
    }
}

在上述示例中,我们使用享元模式来共享相同的文本字符对象。通过工厂类 CharacterFactory 来管理对象池,并根据需要返回共享的对象。在 TextProcessor 类中,我们获取了三个字符对象:‘A’、‘B’ 和 ‘A’,其中第一个和第三个对象是相同的,通过共享对象来节省内存空间。

我们来看一个故事,加深一下理解

假设你是一个游戏开发者,正在开发一个角色扮演游戏。在游戏中,有许多不同类型的怪物,每个怪物都有自己的外观和属性。为了提高游戏的性能和减少内存占用,你决定使用享元模式来管理怪物对象。

在游戏中,有三种类型的怪物:巨人、骷髅和史莱姆。每个怪物都有自己的外观和属性,例如巨人有高耐力和强力攻击,骷髅具有高速度和弱攻击,史莱姆则具有低耐力和中等攻击。

为了实现享元模式,首先你需要创建一个抽象的怪物类 Monster,它包含了共享的状态和行为:

public abstract class Monster {
    protected String appearance;
    protected int health;
    protected int attack;

    public void display() {
        System.out.println("Appearance: " + appearance);
        System.out.println("Health: " + health);
        System.out.println("Attack: " + attack);
    }

    public abstract void attack();
}

然后,你可以实现具体的怪物类,包括巨人、骷髅和史莱姆。这些具体的怪物类只需实现它们特定的属性,而共享的属性则可以在抽象类中定义:

public class GiantMonster extends Monster {
    public GiantMonster() {
        appearance = "Giant";
        health = 100;
        attack = 50;
    }

    @Override
    public void attack() {
        System.out.println("Giant monster is attacking!");
    }
}

public class SkeletonMonster extends Monster {
    public SkeletonMonster() {
        appearance = "Skeleton";
        health = 50;
        attack = 10;
    }

    @Override
    public void attack() {
        System.out.println("Skeleton monster is attacking!");
    }
}

public class SlimeMonster extends Monster {
    public SlimeMonster() {
        appearance = "Slime";
        health = 20;
        attack = 20;
    }

    @Override
    public void attack() {
        System.out.println("Slime monster is attacking!");
    }
}

接下来,你需要创建一个怪物工厂类 MonsterFactory,用于管理和共享怪物对象。它包含一个怪物对象池 monsterPool,使用 HashMap 来存储共享的怪物对象:

public class MonsterFactory {
    private Map<String, Monster> monsterPool;

    public MonsterFactory() {
        monsterPool = new HashMap<>();
    }

    public Monster getMonster(String type) {
        Monster monster = monsterPool.get(type);

        if (monster == null) {
            switch (type) {
                case "giant":
                    monster = new GiantMonster();
                    break;
                case "skeleton":
                    monster = new SkeletonMonster();
                    break;
                case "slime":
                    monster = new SlimeMonster();
                    break;
                default:
                    throw new IllegalArgumentException("Invalid monster type!");
            }

            monsterPool.put(type, monster);
        }

        return monster;
    }
}

最后,你可以使用享元模式来创建和展示怪物:

public class Game {
    public static void main(String[] args) {
        MonsterFactory monsterFactory = new MonsterFactory();

        Monster giant1 = monsterFactory.getMonster("giant");
        giant1.display();

        Monster skeleton = monsterFactory.getMonster("skeleton");
        skeleton.display();

        Monster slime1 = monsterFactory.getMonster("slime");
        slime1.display();

        Monster giant2 = monsterFactory.getMonster("giant");
        giant2.display();
    }
}

在上述示例中,我们使用享元模式来共享相同的怪物对象。通过工厂类 MonsterFactory 来管理对象池,并根据需要返回共享的对象。在 Game 类中,我们获取了四个怪物对象:巨人、骷髅、史莱姆和另一个巨人。其中第一个和最后一个怪物对象是相同的,通过共享对象来节省内存空间。

通过这个故事中的角色扮演游戏的例子,希望你能更好地理解享元模式的概念和应用,以及它如何通过共享对象来减少内存消耗和提高性能。

当使用享元模式时,我们可以总结如下:

  1. 享元模式的目标是通过共享对象来减少内存使用和提高性能。
  2. 享元模式适用于需要创建大量相似对象的情况。
  3. 享元模式通过将对象的共享状态外部化,使得多个对象可以共享相同的状态,从而减少了对象的数量。
  4. 共享状态是不可变的,如果一个对象的状态发生改变,可能会影响到其他对象。
  5. 享元模式的核心是一个工厂类,用于管理和共享对象。
  6. 工厂类通过对象池来存储共享的对象,使用合适的数据结构(如HashMap)来实现快速查找和存储。
  7. 客户端通过工厂类获取共享对象,并可以根据需要设置对象的非共享状态。
  8. 享元模式可以减少内存消耗、提高系统性能,并增加对象的复用性。
  9. 在多线程环境下使用享元模式时,需要考虑线程安全问题,并进行适当的同步处理。
    总之,享元模式通过共享相同状态的对象来减少内存占用,提高性能,并增加对象的复用性。它适用于需要创建大量相似对象的场景,特别是当对象的大部分状态可以外部化时。

文章来源:https://blog.csdn.net/qq_42262444/article/details/135213831
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。