编程自学指南:java程序设计开发,Java 享元模式(Flyweight),享元模式的概念和核心思想,使用享元模式优化系统的内存使用,享元模式的结构和角色

编程自学指南:java程序设计开发,Java 享元模式(Flyweight)详解

一、课程信息

学习目标

  1. 理解享元模式的概念和核心思想。
  2. 掌握享元模式的结构和各个角色的作用。
  3. 学会使用享元模式优化系统的内存使用。
  4. 能够识别并应用享元模式解决实际开发中的问题。

二、课程导入

生活实例引入

  • 以围棋游戏为例,围棋棋盘上有大量的黑子和白子。如果为每一个棋子都创建一个独立的对象,那么会占用大量的内存空间。实际上,所有的黑子都是相同的,所有的白子也是相同的,我们可以只创建一个黑子对象和一个白子对象,然后在需要使用棋子的时候,复用这两个对象,这样就可以大大节省内存空间。
  • 提问学生生活中还有哪些类似的场景,引导他们思考如何在编程中实现对象的复用。

三、享元模式的基本概念

定义

享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存的使用。享元模式将对象的状态分为内部状态和外部状态,内部状态是对象可共享的部分,存储在享元对象内部,不会随环境的改变而改变;外部状态是对象依赖的一个标记,随环境的改变而改变,不可共享。通过共享内部状态,避免了大量相同对象的创建,从而节省了内存空间。

核心思想

将对象的状态分为内部状态和外部状态,共享内部状态,将外部状态作为参数传递给享元对象,从而实现对象的复用。

享元模式的好处

  • 减少对象的创建,降低内存的使用,提高系统的性能。
  • 提高对象的复用性,减少系统的开销。

四、享元模式的结构和角色

结构

享元模式主要由以下四个角色组成:

  1. 抽象享元角色(Flyweight):定义了享元对象的接口,声明了享元对象的操作方法,通常包含一个用于设置外部状态的方法。
  2. 具体享元角色(Concrete Flyweight):实现了抽象享元角色的接口,存储了内部状态,并且可以根据外部状态进行相应的操作。
  3. 享元工厂角色(Flyweight Factory):负责创建和管理享元对象,维护一个享元对象池,当需要使用享元对象时,先从对象池中查找,如果存在则直接返回,否则创建一个新的享元对象并放入对象池中。
  4. 客户端角色(Client):使用享元对象,通过享元工厂获取享元对象,并为其设置外部状态。

角色关系图

可以使用简单的 UML 图展示四个角色之间的关系,帮助学生理解。

示例代码结构

java

// 抽象享元角色
interface Flyweight {
    void operation(int extrinsicState);
}

// 具体享元角色
class ConcreteFlyweight implements Flyweight {
    private final String intrinsicState;

    public ConcreteFlyweight(String intrinsicState) {
        this.intrinsicState = intrinsicState;
    }

    @Override
    public void operation(int extrinsicState) {
        System.out.println("内部状态: " + intrinsicState + ", 外部状态: " + extrinsicState);
    }
}

// 享元工厂角色
class FlyweightFactory {
    private final java.util.HashMap<String, Flyweight> flyweightPool = new java.util.HashMap<>();

    public Flyweight getFlyweight(String key) {
        Flyweight flyweight = flyweightPool.get(key);
        if (flyweight == null) {
            flyweight = new ConcreteFlyweight(key);
            flyweightPool.put(key, flyweight);
        }
        return flyweight;
    }
}

// 客户端角色
public class Client {
    public static void main(String[] args) {
        FlyweightFactory factory = new FlyweightFactory();

        Flyweight flyweight1 = factory.getFlyweight("A");
        flyweight1.operation(1);

        Flyweight flyweight2 = factory.getFlyweight("A");
        flyweight2.operation(2);

        Flyweight flyweight3 = factory.getFlyweight("B");
        flyweight3.operation(3);
    }
}

代码解释

  • Flyweight 是抽象享元角色,定义了享元对象的操作方法 operation(),接收一个外部状态作为参数。
  • ConcreteFlyweight 是具体享元角色,实现了 Flyweight 接口,存储了内部状态 intrinsicState,并在 operation() 方法中根据外部状态进行相应的操作。
  • FlyweightFactory 是享元工厂角色,负责创建和管理享元对象,使用一个 HashMap 作为享元对象池,通过 getFlyweight() 方法根据键获取享元对象,如果对象池中不存在则创建一个新的对象并放入对象池中。
  • Client 是客户端角色,通过享元工厂获取享元对象,并为其设置外部状态。

五、实际案例分析

案例一:围棋游戏

java

// 抽象享元角色:棋子
interface ChessPiece {
    void display(int x, int y);
}

// 具体享元角色:黑子
class BlackChessPiece implements ChessPiece {
    @Override
    public void display(int x, int y) {
        System.out.println("黑子在位置 (" + x + ", " + y + ")");
    }
}

// 具体享元角色:白子
class WhiteChessPiece implements ChessPiece {
    @Override
    public void display(int x, int y) {
        System.out.println("白子在位置 (" + x + ", " + y + ")");
    }
}

// 享元工厂角色:棋子工厂
class ChessPieceFactory {
    private static final java.util.HashMap<String, ChessPiece> chessPiecePool = new java.util.HashMap<>();

    public static ChessPiece getChessPiece(String color) {
        ChessPiece chessPiece = chessPiecePool.get(color);
        if (chessPiece == null) {
            if ("black".equals(color)) {
                chessPiece = new BlackChessPiece();
            } else if ("white".equals(color)) {
                chessPiece = new WhiteChessPiece();
            }
            chessPiecePool.put(color, chessPiece);
        }
        return chessPiece;
    }
}

// 客户端角色
public class GoGame {
    public static void main(String[] args) {
        ChessPiece blackPiece1 = ChessPieceFactory.getChessPiece("black");
        blackPiece1.display(1, 2);

        ChessPiece blackPiece2 = ChessPieceFactory.getChessPiece("black");
        blackPiece2.display(3, 4);

        ChessPiece whitePiece1 = ChessPieceFactory.getChessPiece("white");
        whitePiece1.display(5, 6);
    }
}

代码解释

  • ChessPiece 是抽象享元角色,定义了棋子的显示方法 display(),接收棋子的位置(外部状态)作为参数。
  • BlackChessPiece 和 WhiteChessPiece 是具体享元角色,实现了 ChessPiece 接口,分别表示黑子和白子,在 display() 方法中根据位置信息显示棋子的位置。
  • ChessPieceFactory 是享元工厂角色,负责创建和管理棋子对象,使用一个 HashMap 作为棋子对象池,通过 getChessPiece() 方法根据颜色获取棋子对象,如果对象池中不存在则创建一个新的对象并放入对象池中。
  • GoGame 是客户端角色,通过棋子工厂获取棋子对象,并为其设置位置信息。

案例二:文本编辑器中的字符显示

// 抽象享元角色:字符
interface Character {
    void display(int fontSize);
}

// 具体享元角色:字母 A
class CharacterA implements Character {
    @Override
    public void display(int fontSize) {
        System.out.println("字符 A,字号: " + fontSize);
    }
}

// 具体享元角色:字母 B
class CharacterB implements Character {
    @Override
    public void display(int fontSize) {
        System.out.println("字符 B,字号: " + fontSize);
    }
}

// 享元工厂角色:字符工厂
class CharacterFactory {
    private static final java.util.HashMap<Character, Character> characterPool = new java.util.HashMap<>();

    public static Character getCharacter(char key) {
        Character character = characterPool.get(key);
        if (character == null) {
            switch (key) {
                case 'A':
                    character = new CharacterA();
                    break;
                case 'B':
                    character = new CharacterB();
                    break;
                default:
                    throw new IllegalArgumentException("不支持的字符: " + key);
            }
            characterPool.put(key, character);
        }
        return character;
    }
}

// 客户端角色
public class TextEditor {
    public static void main(String[] args) {
        Character charA1 = CharacterFactory.getCharacter('A');
        charA1.display(12);

        Character charA2 = CharacterFactory.getCharacter('A');
        charA2.display(16);

        Character charB1 = CharacterFactory.getCharacter('B');
        charB1.display(20);
    }
}

代码解释

  • Character 是抽象享元角色,定义了字符的显示方法 display(),接收字号(外部状态)作为参数。
  • CharacterA 和 CharacterB 是具体享元角色,实现了 Character 接口,分别表示字母 A 和字母 B,在 display() 方法中根据字号信息显示字符和字号。
  • CharacterFactory 是享元工厂角色,负责创建和管理字符对象,使用一个 HashMap 作为字符对象池,通过 getCharacter() 方法根据字符获取字符对象,如果对象池中不存在则创建一个新的对象并放入对象池中。
  • TextEditor 是客户端角色,通过字符工厂获取字符对象,并为其设置字号信息。

六、享元模式的优缺点和适用场景

优点

  • 减少对象的创建,降低内存的使用,提高系统的性能。
  • 提高对象的复用性,减少系统的开销。
  • 符合开闭原则,方便扩展新的享元对象。

缺点

  • 增加了系统的复杂度,需要区分内部状态和外部状态,并且需要维护一个享元对象池。
  • 可能会导致线程安全问题,因为多个客户端可能会同时访问和修改享元对象的外部状态。

适用场景

  • 当系统中存在大量相似对象,并且这些对象的内部状态可以共享时,可以使用享元模式。
  • 当创建对象的成本较高,并且需要频繁创建和销毁对象时,可以使用享元模式。
  • 当对象的状态可以分为内部状态和外部状态,并且外部状态可以在运行时动态设置时,可以使用享元模式。

七、课堂练习

练习一

  • 设计一个享元模式的示例,模拟图形绘制系统。图形包括圆形、方形和三角形,每种图形有不同的颜色。使用享元模式优化图形对象的创建,避免重复创建相同颜色的图形对象。

练习二

  • 扩展文本编辑器中的字符显示案例,增加一个新的字符类型(如字母 C),并修改享元工厂的实现以支持新字符的创建和管理。

八、课程总结

知识回顾

  • 回顾享元模式的概念、核心思想和结构。
  • 总结享元模式的优缺点和适用场景。
  • 强调享元模式在优化内存使用和提高系统性能方面的重要性。

常见问题解答

  • 解答学生在课堂练习和学习过程中遇到的问题。

口诀总结

  • “享元模式巧复用,内外状态要分清。内部共享省内存,外部参数来搞定。工厂管理对象池,创建查找很轻松。复杂系统性能高,合理运用显神通。”

九、课后作业

作业一

  • 设计一个享元模式的示例,模拟数据库连接池。数据库连接对象包含连接信息(如数据库地址、用户名、密码等),使用享元模式优化数据库连接对象的创建和管理,避免重复创建相同连接信息的连接对象。

作业二

  • 思考在实际项目中,还有哪些场景可以使用享元模式来优化代码结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zl515035644

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

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

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

打赏作者

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

抵扣说明:

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

余额充值