设计模式系列-享元模式-Flyweight

享元模式介绍

我们用Java 编程时经常听说 “从对象的角度思考” 和 “一切皆是对象” 这样的金句,因为在开发应用时对象设计是最小颗粒度的设计,好的设计可以提升系统的灵活性。但是,作为一个好的程序员也需要根据对象所消耗的资源总量来考虑应用的性能,当你处理大量的对象时,你需要关注对象将要消耗的资源,包括内存的占用和使用CPU的时间。

假如在一个赛程游戏里你需要1000个汽车对象,这些对象唯一的不同点是在赛道上的某时刻的位置信息,为了避免创建1000个汽车对象或者当更多用户加入游戏时创建更多的对象,你可以创建包含共有信息的汽车对象单例和一个包含汽车位置状态的客户端对象。享元模式减少重复的数据,因此在创建大量对象时可以减少内存的占用。

享元模式是一个结构型的设计模式,利用共享对象的概念实现,开发应用时,如果需要创建大量的细粒度的对象,这时应该采用共享的方式而不是重复创建,被共享的对象我们称为 flyweight

为了更好的理解享元模式,我们需要理解两个概念:

  • 固有数据:存储在享元对象里,和其他对象共享
  • 非固有数据:不能共享的数据,存在客户度对象里,用来被计算或者传入享元对象

享元模式的参与者

继续拿赛车游戏来说,我们需要两种赛车:Midget 和 Sprint , 首先创建一个基准抽象类 RaceCar , 包含3个字段:

  • name 汽车的名称,String 类型
  • speed 汽车最大速度,int 类型
  • horsepower 马力,int 类型

子类,如FlyweightMidgetCar 对三个字段存的值为:

name="Midget Car";
speed=140;
horsepower=400;

这些字段是 FlyweightMidgetCar 的固有数据,被不同的对象共享,不会被其他共享的对象所改变。
在基类中,定义了一个抽象方法 moveCar(int currentX, int currentY, int newX ,int newY) ,子类重写此方法,汽车的位置在不同的对象之间是不同的,我们成为非固有数据。
为了管理享元对象,我们需要一个工厂类CarFactory,此类需要一个池子来存储享元对象,当客户端第一次获取对象时用固有数据初始化对象,初始化后放入池子然后返回给客户端。
我们还需一个客户端类RaceCarClient ,此类会从 CarFactory 获取享元对象,并把非固有数据传给享元对象,在此类中,将把汽车的当前位置传给方法 moveCar(int currentX, int currentY, int newX ,int newY)

现在我们把此例子中的角色按照享元模式的参与者进行归类:

  • Flyweight(RaceCar): 创建享元对象的抽象类或接口,定义方法来接收非固有数据
  • ConcreteFlyweight(FlyweightMidgetCar 和 FlyweightSprintCar): 集成或实现Flyweight ,用来创建具体的享元对象
  • FlyweightFactory(CarFactory): 创建和管理享元对象
  • Client (RaceCarClient) : 请求 FlyweightFactory 获取享元对象

享元模式的实现

public abstract class RaceCar {
        /*Intrinsic state  stored and shared in the Flyweight object*/
        String name;
        int speed;
        int horsePower;
       /* Extrinsic state is stored or computed by client objects, and passed to the Flyweight.*/
        abstract void moveCar(int currentX, int currentY, int newX ,int newY);
}

public class FlyweightSprintCar extends RaceCar{
    /*Track number of flyweight instantiation*/
    public static int num;
    public FlyweightSprintCar()
    {
        num++;
    }

   /*This method accepts car location (extrinsic). No reference to current
    *or new location is maintained inside the flyweight implementation*/
    @Override
    public void moveCar(int currentX, int currentY, int newX ,int newY)
    {
        System.out.println("New location of "+this.name+" is X"+newX + " - Y"+newY);
    }
}

public class FlyweightMidgetCar extends RaceCar {
    /*Track number of flyweight instantiation*/
        public static int num;
        public FlyweightMidgetCar()
        {
            num++;
        }
    /*This method accepts car location (extrinsic). No reference to current
    *or new location is maintained inside the flyweight implementation*/
        @Override
        public void moveCar(int currentX, int currentY, int newX ,int newY)
        {
            System.out.println("New location of "+this.name+" is X"+newX + " - Y"+newY);
        }
}

public class CarFactory {
    private static Map<String, RaceCar> flyweights = new HashMap<>();
    /*If key exist, return flyweight from Map*/
    public static RaceCar getRaceCar(String key) {
        if(flyweights.containsKey(key)){
            return flyweights.get(key);
        }
        RaceCar raceCar;
        /*If key does not exist in Map, create flyweight, put it in Map, and return the object*/
        switch (key)
        {
            case "Midget":
                raceCar = new FlyweightMidgetCar();
                raceCar.name="Midget Car";
                raceCar.speed=140;
                raceCar.horsePower=400;
                break;
            case "Sprint":
                raceCar = new FlyweightSprintCar();
                raceCar.name="Sprint Car";
                raceCar.speed=160;
                raceCar.horsePower=1000;
                break;
            default:
                throw new IllegalArgumentException("Unsupported car type.");
        }
        flyweights.put(key, raceCar);
        return raceCar;
    }
}

public class RaceCarClient {
    private RaceCar raceCar;
    public RaceCarClient(String name) {
        /*Ask factory for a RaceCar*/
        raceCar = CarFactory.getRaceCar(name);
    }
     /**
     * The extrinsic state of the flyweight is maintained by the client
     */
    private int currentX = 0;
    private int currentY=0;
    public void moveCar(int newX, int newY){
    /*Car movement is handled by the flyweight object*/
        raceCar.moveCar(currentX,
                currentY, newX, newY);
        currentX = newX;
        currentY = newY;
    }

}

public class RaceCarClientTest {

    @Test
    public void testRaceCar() throws Exception {
        RaceCarClient raceCars[] = {
                new RaceCarClient("Midget"),
                new RaceCarClient("Midget"),
                new RaceCarClient("Midget"),
                new RaceCarClient("Sprint"),
                new RaceCarClient("Sprint"),
                new RaceCarClient("Sprint")
        };
        raceCars[0].moveCar(29, 3112);
        raceCars[1].moveCar(39, 2002);
        raceCars[2].moveCar(49, 1985);
        raceCars[3].moveCar(59, 2543);
        raceCars[4].moveCar(69, 2322);
        raceCars[5].moveCar(79, 2135);
       /*Output and observe the number of instances created*/
       System.out.println("Midget Car Instances: " + FlyweightMidgetCar.num);
       System.out.println("Sprint Car Instances: " + FlyweightSprintCar.num);
    }
}

运行单元测试:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running guru.springframework.gof.flyweight.RaceCarClientTest
New location of Midget Car is X29 - Y3112
New location of Midget Car is X39 - Y2002
New location of Midget Car is X49 - Y1985
New location of Sprint Car is X59 - Y2543
New location of Sprint Car is X69 - Y2322
New location of Sprint Car is X79 - Y2135
Midget Car Instances: 1
Sprint Car Instances: 1
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec - in guru.springframework.gof.flyweight.RaceCarClientTest

Jdk 中使用享元模式的例子:
Java.lang.Integer#valueOf(int) (also on Boolean, Byte, Character, Short and Long)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值