• 「补课」进行时:设计模式(21)——享元模式


    1. 前文汇总

    「补课」进行时:设计模式系列

    2. 享元模式

    2.1 定义

    享元模式(Flyweight Pattern)很简单,它解决的需求也很直接,同时它也是池技术的重要实现方式,先看下它的定义:

    Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。)

    2.2 通用类图

    • Flyweight 抽象享元角色:它是一个产品的抽象类, 同时定义出对象的外部状态和内部状态的接口或实现。
    • ConcreteFlyweight 具体享元角色:具体的一个产品类, 实现抽象角色定义的业务。
    • unsharedConcreteFlyweight 不可共享的享元角色:不存在外部状态或者安全要求(如线程安全) 不能够使用共享技术的对象, 该对象一般不会出现在享元工厂中。
    • FlyweightFactory 享元工厂:它的职责非常简单, 就是构造一个池容器, 同时提供从池中获得对象的方法。

    2.3 通用代码

    抽象享元角色:

    public abstract class Flyweight {
        // 内部状态
        private String intrinsic;
        // 外部状态
        protected final String extrinsic;
        // 要求享元角色必须接受外部状态
        protected Flyweight(String extrinsic) {
            this.extrinsic = extrinsic;
        }
        // 定义业务操作
        abstract void operate();
    
        public String getIntrinsic() {
            return intrinsic;
        }
    
        public void setIntrinsic(String intrinsic) {
            this.intrinsic = intrinsic;
        }
    }
    

    具体享元角色:

    public class ConcreteFlyweight1 extends Flyweight{
        protected ConcreteFlyweight1(String extrinsic) {
            super(extrinsic);
        }
    
        @Override
        void operate() {
    
        }
    }
    
    public class ConcreteFlyweight2 extends Flyweight{
        protected ConcreteFlyweight2(String extrinsic) {
            super(extrinsic);
        }
    
        @Override
        void operate() {
    
        }
    }
    

    享元工厂:

    public class FlyweightFactory {
        // 定义一个池容器
        private static HashMap<String,Flyweight> pool = new HashMap<>();
        // 享元工厂
        public static Flyweight getFlyweight(String Extrinsic) {
            // 需要返回的对象
            Flyweight flyweight = null;
            // 在池中没有该对象
            if(pool.containsKey(Extrinsic)) {
                flyweight = pool.get(Extrinsic);
            } else {
                // 根据外部状态创建享元对象
                flyweight = new ConcreteFlyweight1(Extrinsic);
                // 放置到池中
                pool.put(Extrinsic, flyweight);
            }
            return flyweight;
        }
    }
    

    2.4 优缺点

    享元模式是一个非常简单的模式, 它可以大大减少应用程序创建的对象, 降低程序内存的占用, 增强程序的性能, 但它同时也提高了系统复杂性, 需要分离出外部状态和内部状态, 而且外部状态具有固化特性, 不应该随内部状态改变而改变, 否则导致系统的逻辑混乱。

    3. 一个小例子

    享元模式很简单,上面的通用代码其实就是一个很好的示例,类似于 Java 中的 String 常量池,没有的对象创建后存在池中,若池中存在该对象则直接从池中取出。

    我这里还是再举一个简单的例子,比如接了我一个小型的外包项目,是做一个产品展示网站,后来他的朋友们也希望做这样的网站,但要求都有些不同,我们当然不能直接复制粘贴再来一份,有人希望是视频站,有人希望是图文站等等,而且因为经费原因不能每个网站租用一个空间。

    这种事情在生活中很长见,不过大多数情况都是直接 copy 一份代码,再做做改动,但是在享元模式中,就不存在这种情况啦~~~

    网站抽象类:

    public abstract class WebSite {
        abstract void use();
    }
    

    具体网站类:

    public class ConcreteWebSite extends WebSite {
    
        private String name;
    
        public ConcreteWebSite(String name) {
            this.name = name;
        }
    
        @Override
        void use() {
            System.out.println("网站分类:" + name);
        }
    }
    

    网络工厂类:

    public class WebSiteFactory {
        private HashMap<String, WebSite> pool = new HashMap<>();
    
        //获得网站分类
        public WebSite getWebSiteCategory(String key) {
            if(!pool.containsKey(key)) {
                pool.put(key, new ConcreteWebSite(key));
            }
            return pool.get(key);
        }
    
        //获得网站分类总数
        public int getWebSiteCount() {
            return pool.size();
        }
    }
    

    Client 客户端:

    public class Client {
        public static void main(String[] args) {
            WebSiteFactory factory = new WebSiteFactory();
    
            WebSite fx = factory.getWebSiteCategory("视频站");
            fx.use();
    
            WebSite fy = factory.getWebSiteCategory("视频站");
            fy.use();
    
            WebSite fz = factory.getWebSiteCategory("视频站");
            fz.use();
    
            WebSite fa = factory.getWebSiteCategory("图文站");
            fa.use();
    
            WebSite fb = factory.getWebSiteCategory("图文站");
            fb.use();
    
            WebSite fc = factory.getWebSiteCategory("图文站");
            fc.use();
    
            System.out.println("网站分类总数为:" + factory.getWebSiteCount());
        }
    }
    

    执行结果:

    网站分类:视频站
    网站分类:视频站
    网站分类:视频站
    网站分类:图文站
    网站分类:图文站
    网站分类:图文站
    网站分类总数为:2
    

    可以看出,虽然我们做了 6 个网站,但网站分类只有 2 个。

    这样基本算是实现了享元模式的共享对象的目的,但是这里实际上没有体现对象间的不同。

    我们再加入一个用户类:

    public class User {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    然后对 WebSiteConcreteWebSiteuse() 方法进行修改,添加 User 参数:

    public abstract class WebSite {
        abstract void use(User user);
    }
    
    public class ConcreteWebSite extends WebSite {
    
        private String name;
    
        public ConcreteWebSite(String name) {
            this.name = name;
        }
    
        @Override
        void use(User user) {
            System.out.println("网站分类:" + name + " 用户:" + user.getName());
        }
    }
    

    最后修改一下 Client 类:

    public class Client {
        public static void main(String[] args) {
            WebSiteFactory factory = new WebSiteFactory();
    
            WebSite fx = factory.getWebSiteCategory("视频站");
            fx.use(new User("tom"));
    
            WebSite fy = factory.getWebSiteCategory("视频站");
            fy.use(new User("cat"));
    
            WebSite fz = factory.getWebSiteCategory("视频站");
            fz.use(new User("nginx"));
    
            WebSite fa = factory.getWebSiteCategory("图文站");
            fa.use(new User("apache"));
    
            WebSite fb = factory.getWebSiteCategory("图文站");
            fb.use(new User("netty"));
    
            WebSite fc = factory.getWebSiteCategory("图文站");
            fc.use(new User("jboss"));
    
            System.out.println("网站分类总数为:" + factory.getWebSiteCount());
        }
    }
    

    最终结果:

    网站分类:视频站 用户:tom
    网站分类:视频站 用户:cat
    网站分类:视频站 用户:nginx
    网站分类:图文站 用户:apache
    网站分类:图文站 用户:netty
    网站分类:图文站 用户:jboss
    网站分类总数为:2
    

    这样就可以协调内部与外部状态,哪怕接手了上千个网站的需求,只要要求相同或类似,实际开发代码也就是分类的那几种。

  • 相关阅读:
    委托示例一则
    JQuery学习笔记(3)JQuery中的DOM操作
    JQuery学习笔记(2)JQuery选择器
    把.net的web开发补起来,徐图之
    今天把swagger补了一下
    Azure Cosmos DB Core (SQL)
    wcf callback channel问题多多
    gitlab安装
    Kubernetes
    搜索功能实现
  • 原文地址:https://www.cnblogs.com/babycomeon/p/14182493.html
Copyright © 2020-2023  润新知