Java代码设计模式讲解二十三种设计模式(八)

简介: Java代码设计模式讲解二十三种设计模式

(3)代码示例

生活中,我们在买手抓饼一般有时候会选择加鸡蛋或者加火腿,我们就用代码来实现这个流程

  • 最基础的煎饼类 Battercake
package com.alibaba.design.decoratorpattern.battercake.v1;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:13
 */
public class Battercake {
    protected String getMsg(){
        return "煎饼";
    }
    public int getPrice(){
        return 5;
    }
}
  • 煎饼加鸡蛋类 BatterakeWithEgg
package com.alibaba.design.decoratorpattern.battercake.v1;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:14
 */
public class BattercakeWithEgg extends Battercake {
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }
    @Override
    //加一个鸡蛋加1块钱
    public int getPrice() {
        return super.getPrice() + 1;
    }
}
  • 煎饼加鸡蛋和火腿 BattercakeWithEggAndSausage
package com.alibaba.design.decoratorpattern.battercake.v1;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:15
 */
public class BattercakeWithEggAndSausage extends Battercake {
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }
    @Override
    //加一个香肠加2块钱
    public int getPrice() {
        return super.getPrice() + 2;
    }
}

客户端测试类

package com.alibaba.design.decoratorpattern.battercake.v1;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:15
 */
public class BattercakeTest  {
    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg() + ",总价格:" + battercake.getPrice());
        Battercake battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg() + ",总价格:" + battercakeWithEgg.getPrice());
        Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格:" + battercakeWithEggAndSausage.getPrice());
    }
}

初步一看似乎没啥问题,但是,如果用户需要一个加 2 个鸡蛋加 1 根香肠的煎饼,那么用我们现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求再变,一直加定制显然是不科学的。那么下面我们就用装饰者模式来解决上面的问题。首先创建一个建煎饼的抽象 Battercake 类:

package com.alibaba.design.decoratorpattern.battercake.v2;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:23
 */
public abstract class Battercake {
    protected abstract String getMsg();
    protected abstract int getPrice();
}
  • 创建一个基本的煎饼(或者叫基础套餐)BaseBattercake:
package com.alibaba.design.decoratorpattern.battercake.v2;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:24
 */
public class BaseBattercake extends Battercake {
    @Override
    protected String getMsg(){
        return "煎饼";
    }
    @Override
    public int getPrice(){
        return 5;
    }
}

然后,再创建一个扩展套餐的抽象装饰者 BattercakeDecotator 类:

  • BattercakeDecorator
package com.alibaba.design.decoratorpattern.battercake.v2;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:25
 */
public abstract class BattercakeDecorator extends Battercake {
    //静态代理,委派
    private Battercake battercake;
    public BattercakeDecorator(Battercake battercake) {
        this.battercake = battercake;
    }
    protected abstract void doSomething();
    @Override
    protected String getMsg() {
        return this.battercake.getMsg();
    }
    @Override
    protected int getPrice() {
        return this.battercake.getPrice();
    }
}

然后,创建鸡蛋装饰者 EggDecorator 类:

  • EggDecorator
package com.alibaba.design.decoratorpattern.battercake.v2;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:26
 */
public class EggDecorator extends BattercakeDecorator {
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }
    @Override
    protected void doSomething() {
    }
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }
    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}

创建香肠装饰者 SausageDecorator 类:

  • SausageDecorator
package com.alibaba.design.decoratorpattern.battercake.v2;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:26
 */
public class SausageDecorator extends BattercakeDecorator {
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }
    @Override
    protected void doSomething() {
    }
    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }
    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}
  • 客户端测试类 BattercakeTest
package com.alibaba.design.decoratorpattern.battercake.v2;
/**
 * @author zhouyanxiang
 * @create 2020-07-2020/7/29-22:27
 */
public class BattercakeTest {
    public static void main(String[] args) {
        Battercake battercake;
        //路边摊买一个煎饼
        battercake = new BaseBattercake();
        //煎饼有点小,想再加一个鸡蛋
        battercake = new EggDecorator(battercake);
        //再加一个鸡蛋
//        battercake = new EggDecorator(battercake);
        //很饿,再加根香肠
        battercake = new SausageDecorator(battercake);
        battercake = new SausageDecorator(battercake);
        battercake = new SausageDecorator(battercake);
        battercake = new SausageDecorator(battercake);
        battercake = new SausageDecorator(battercake);
        //跟静态代理最大区别就是职责不同
        //静态代理不一定要满足is-a的关系
        //静态代理会做功能增强,同一个职责变得不一样
        //装饰器更多考虑是扩展
        System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
    }
}

整个类图

装饰者模式最本质的特征是讲原有类的附加功能抽离出来,简化原有类的逻辑。通过这样两个案例,我们可以总结出来,其实抽象的装饰者是可有可无的,具体可以根据业务模型来选择。

装饰者模式和适配器模式对比

装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式

装饰者模式 适配器模式
形式 是一种非常特别的适配器模式 没有层级关系,装饰器模式有层级关系
定义 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留 OOP 关系 适配器和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装
关系 满足 is-a 的关系 满足 has-a 的关系
功能 注重覆盖、扩展 注重兼容、转换
设计 前置考虑 后置考虑

(4)模式在源码中的体现

装饰器模式在源码中也应用得非常多,在 JDK 中体现最明显的类就是 IO 相关的类,如BufferedReader、InputStream、OutputStream,看一下常用的 InputStream 的类结构图

在 Spring 中的 TransactionAwareCacheDecorator 类我们也可以来尝试理解一下,这个类主要是用来处理事务缓存的,来看一下代码:

public class TransactionAwareCacheDecorator implements Cache {
  private final Cache targetCache;
    public TransactionAwareCacheDecorator(Cache targetCache) {
        Assert.notNull(targetCache, "Target Cache must not be null");
             this.targetCache = targetCache;
        }
        public Cache getTargetCache() {
             return this.targetCache;
        }
        ...
    }

TransactionAwareCacheDecorator 就是对 Cache 的一个包装。再来看一个 MVC 中的装饰者模式 HttpHeadResponseDecorator 类:

public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator {
    public HttpHeadResponseDecorator(ServerHttpResponse delegate) {
      super(delegate);
    }
    ...
}

最后,看看 MyBatis 中的一段处理缓存的设计 org.apache.ibatis.cache.Cache 类,找到它的包定位:

从名字上来看其实更容易理解了。比如 FIFO Cache 先入先出算法的缓存;LRU Cache 最近最少使用的缓存;TransactionlCache 事务相关的缓存,都是采用装饰者模式。

(5)装饰器模式的优缺点

  • 优点:
    1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象
    扩展功能,即插即用。
    2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
    3、装饰者完全遵守开闭原则。
  • 缺点:
    1、会出现更多的代码,更多的类,增加程序复杂性。
    2、动态装饰时,多层装饰时会更复杂。
    那么装饰者模式我们就讲解到这里,希望小伙伴们认真体会,加深理解。

3.4 桥接模式

(1)概念

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

(2)适用场景

1、如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。 2、对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 3、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

注意事项:对于两个独立变化的维度,使用桥接模式再适合不过了。

(3)代码示例

我们有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircleGreenCircleShape 是一个抽象类,将使用 DrawAPI 的对象。BridgePatternDemo,我们的演示类使用 Shape 类来画出不同颜色的圆。

创建桥接实现接口。

package com.alibaba.design.bridgepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:37
 */
public interface DrawAPI {
    public void drawCircle(int radius, int x, int y);
}

创建实现了 DrawAPI 接口的实体桥接实现类。

  • GreenCircle
package com.alibaba.design.bridgepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:38
 */
public class GreenCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: green, radius: " + radius +", x: " +x+", "+ y +"]");
    }
}
  • RedCircle
package com.alibaba.design.bridgepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:38
 */
public class RedCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: red, radius: " + radius +", x: " +x+", "+ y +"]");
    }
}

使用 DrawAPI 接口创建抽象类 Shape

package com.alibaba.design.bridgepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:39
 */
public abstract class Shape {
    protected DrawAPI drawAPI;
    protected Shape(DrawAPI drawAPI){
        this.drawAPI = drawAPI;
    }
    public abstract void draw();
}

创建实现了 Shape 接口的实体类。

package com.alibaba.design.bridgepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:39
 */
public class Circle extends Shape {
    private int x, y, radius;
    public Circle(int x, int y, int radius, DrawAPI drawAPI) {
        super(drawAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    @Override
    public void draw() {
        drawAPI.drawCircle(radius,x,y);
    }
}

使用 ShapeDrawAPI 类画出不同颜色的圆。

package com.alibaba.design.bridgepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:40
 */
public class BridgePatternDemo {
    public static void main(String[] args) {
        Shape redCircle = new Circle(100,100, 10, new RedCircle());
        Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
        redCircle.draw();
        greenCircle.draw();
    }
}

输出结果:

(4)该模式在源码中的体现

如果从桥接模式来看,java.sql.Driver就是一个接口

下面可以有MySQL的Driver,Oracle的Driver,这些就可以当做实现接口类。那么我们现在来看看MySQL中的Driver类

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

特别简短的代码,其实只调用了DriverManager中的registerDriver方法来注册驱动。当驱动注册完成后,我们就会开始调用DriverManager中的getConnection方法了

public class DriverManager {
    public static Connection getConnection(String url,
        String user, String password) throws SQLException {
        java.util.Properties info = new java.util.Properties();
        if (user != null) {
            info.put("user", user);
        }
        if (password != null) {
            info.put("password", password);
        }
        return (getConnection(url, info, Reflection.getCallerClass()));
    }
    private static Connection getConnection(
        String url, java.util.Properties info, Class<?> caller) throws SQLException {
        /*
         * When callerCl is null, we should check the application's
         * (which is invoking this class indirectly)
         * classloader, so that the JDBC driver class outside rt.jar
         * can be loaded from here.
         */
        ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
        synchronized(DriverManager.class) {
            // synchronize loading of the correct classloader.
            if (callerCL == null) {
                callerCL = Thread.currentThread().getContextClassLoader();
            }
        }
        if(url == null) {
            throw new SQLException("The url cannot be null", "08001");
        }
        println("DriverManager.getConnection(\"" + url + "\")");
        // Walk through the loaded registeredDrivers attempting to make a connection.
        // Remember the first exception that gets raised so we can reraise it.
        SQLException reason = null;
        for(DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if(isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }
            } else {
                println("    skipping: " + aDriver.getClass().getName());
            }
        }
        // if we got here nobody could connect.
        if (reason != null)    {
            println("getConnection failed: " + reason);
            throw reason;
        }
        println("getConnection: no suitable driver found for "+ url);
        throw new SQLException("No suitable driver found for "+ url, "08001");
    }
}
}

上面是简化的代码,可以看到需要返回的是Connection对象。在Java中通过Connection提供给各个数据库一样的操作接口,这里的Connection可以看作抽象类。可以说我们用来操作不同数据库的方法都是相同的,不过MySQL有自己的ConnectionImpl类,同样Oracle也有对应的实现类。这里Driver和Connection之间是通过DriverManager类进行桥接的,不是像我们上面说的那样用组合关系来进行桥接。

(5)桥接模式的优缺点

  • 优点:
    1、抽象和实现的分离。
    2、优秀的扩展能力。
    3、实现细节对客户透明。
  • **缺点:**桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

3.5 组合模式

(1)概念

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。

(2)适用场景

部分、整体场景,如树形菜单,文件、文件夹的管理。

1、表示对象的部分-整体层次结构(树形结构)。

2、希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

(3)代码示例

创建 Employee 类,该类带有 Employee 对象的列表。

package com.alibaba.design.compositepattern;
import java.util.ArrayList;
import java.util.List;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-17:59
 */
public class Employee {
    private String name;
    private String dept;
    private int salary;
    private List<Employee> subordinates;
    //构造函数
    public Employee(String name,String dept, int sal) {
        this.name = name;
        this.dept = dept;
        this.salary = sal;
        subordinates = new ArrayList<Employee>();
    }
    public void add(Employee e) {
        subordinates.add(e);
    }
    public void remove(Employee e) {
        subordinates.remove(e);
    }
    public List<Employee> getSubordinates(){
        return subordinates;
    }
    @Override
    public String toString(){
        return ("Employee :[ Name : "+ name
                +", dept : "+ dept + ", salary :"
                + salary+" ]");
    }
}

使用 Employee 类来创建和打印员工的层次结构。

package com.alibaba.design.compositepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:00
 */
public class CompositePatternDemo {
    public static void main(String[] args) {
        Employee CEO = new Employee("John","CEO", 30000);
        Employee headSales = new Employee("Robert","Head Sales", 20000);
        Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
        Employee clerk1 = new Employee("Laura","Marketing", 10000);
        Employee clerk2 = new Employee("Bob","Marketing", 10000);
        Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
        Employee salesExecutive2 = new Employee("Rob","Sales", 10000);
        CEO.add(headSales);
        CEO.add(headMarketing);
        headSales.add(salesExecutive1);
        headSales.add(salesExecutive2);
        headMarketing.add(clerk1);
        headMarketing.add(clerk2);
        //打印该组织的所有员工
        System.out.println(CEO);
        for (Employee headEmployee : CEO.getSubordinates()) {
            System.out.println(headEmployee);
            for (Employee employee : headEmployee.getSubordinates()) {
                System.out.println(employe);
            }
        }
    }
}

(4)该模式在源码中的体现

JDK源码中的应用:

Container类

通过看它的行为方法add()可以看出,它添加的是它的父类,符合组合模式的设计

HashMap类

add()方法中放入的是map,这也是组合模式的体现

ArrayList类

Mybatis中的应用:

MixedSqlNode类

(5)组合模式的优缺点

  • 优点:
    1、高层模块调用简单。
    2、节点自由增加。
  • **缺点:**在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

3.6 外观模式

(1)概念

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。

(2)适用场景

1、为复杂的模块或子系统提供外界访问的模块。

2、子系统相对独立。

3、预防低水平人员带来的风险。

注意事项:在层次化结构中,可以使用外观模式定义系统中每一层的入口。

(3)代码示例

我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。下一步是定义一个外观类 ShapeMaker

ShapeMaker 类使用实体类来代表用户对这些类的调用。FacadePatternDemo,我们的演示类使用 ShapeMaker 类来显示结果。

创建一个接口。

  • Shape
package com.alibaba.design.facadepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:18
 */
public interface Shape {
    void draw();
}

创建实现接口的实体类。

  • Rectangle
package com.alibaba.design.facadepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:18
 */
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Rectangle::draw()");
    }
}
  • Circle
package com.alibaba.design.facadepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:20
 */
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Circle::draw()");
    }
}
  • Square
package com.alibaba.design.facadepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:19
 */
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Square::draw()");
    }
}

创建一个外观类

  • ShapeMaker
package com.alibaba.design.facadepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:20
 */
public class ShapeMaker {
    private Shape circle;
    private Shape rectangle;
    private Shape square;
    public ShapeMaker() {
        circle = new Circle();
        rectangle = new Rectangle();
        square = new Square();
    }
    public void drawCircle(){
        circle.draw();
    }
    public void drawRectangle(){
        rectangle.draw();
    }
    public void drawSquare(){
        square.draw();
    }
}

客户端测试类

package com.alibaba.design.facadepattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:20
 */
public class FacadePatternDemo {
    public static void main(String[] args) {
        ShapeMaker shapeMaker = new ShapeMaker();
        shapeMaker.drawCircle();
        shapeMaker.drawRectangle();
        shapeMaker.drawSquare();
    }
}

输出结果:

(4)该模式在源码中的体现

JDK类库中的外观模式

java.lang.Class

javax.faces.webapp.FacesServlet

Class中的forName(String name, boolean initialize ,ClassLoader loader)方法调用了ClassLoader、System接口。

@CallerSensitive
    public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }

FacesServlet实现了Servlet接口,在实现方法service()中调用了HttpServletRequest、HttpServletResponse、ApplicationContext接口的方法。

(5)外观模式的优缺点

  • 优点:
    1、减少系统相互依赖。
    2、提高灵活性。
    3、提高了安全性。
  • **缺点:**不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

3.7 享元模式

(1)概念

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。

享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。

**主要解决:**在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

(2)适用场景

1、系统中有大量对象。

2、这些对象消耗大量内存。

3、这些对象的状态大部分可以外部化。

4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。

5、系统不依赖于这些对象身份,这些对象是不可分辨的。

注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。

(3)代码示例

创建一个接口。

package com.alibaba.design.flyweightpattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:48
 */
public interface Shape {
    void draw();
}

创建实现接口的实体类。

package com.alibaba.design.flyweightpattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:49
 */
public class Circle implements Shape {
    private String color;
    private int x;
    private int y;
    private int radius;
    public Circle(String color){
        this.color = color;
    }
    public void setX(int x) {
        this.x = x;
    }
    public void setY(int y) {
        this.y = y;
    }
    public void setRadius(int radius) {
        this.radius = radius;
    }
    @Override
    public void draw() {
        System.out.println("Circle: Draw() [Color : " + color
                +", x : " + x +", y :" + y +", radius :" + radius);
    }
}

创建一个工厂,生成基于给定信息的实体类的对象。

package com.alibaba.design.flyweightpattern;
import java.util.HashMap;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:49
 */
public class ShapeFactory {
    private static final HashMap<String, Shape> circleMap = new HashMap<>();
    public static Shape getCircle(String color) {
        Circle circle = (Circle)circleMap.get(color);
        if(circle == null) {
            circle = new Circle(color);
            circleMap.put(color, circle);
            System.out.println("Creating circle of color : " + color);
        }
        return circle;
    }
}

使用该工厂,通过传递颜色信息来获取实体类的对象。

package com.alibaba.design.flyweightpattern;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/2-18:50
 */
public class FlyweightPatternDemo {
    private static final String colors[] =
            { "Red", "Green", "Blue", "White", "Black" };
    public static void main(String[] args) {
        for(int i=0; i < 20; ++i) {
            Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
            circle.setX(getRandomX());
            circle.setY(getRandomY());
            circle.setRadius(100);
            circle.draw();
        }
    }
    private static String getRandomColor() {
        return colors[(int)(Math.random()*colors.length)];
    }
    private static int getRandomX() {
        return (int)(Math.random()*100 );
    }
    private static int getRandomY() {
        return (int)(Math.random()*100);
    }
}

输出结果:

Creating circle of color : Black
Circle: Draw() [Color : Black, x : 36, y :71, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 27, y :27, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 64, y :10, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 15, y :44, radius :100
Circle: Draw() [Color : Green, x : 19, y :10, radius :100
Circle: Draw() [Color : Green, x : 94, y :32, radius :100
Circle: Draw() [Color : White, x : 69, y :98, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 13, y :4, radius :100
Circle: Draw() [Color : Green, x : 21, y :21, radius :100
Circle: Draw() [Color : Blue, x : 55, y :86, radius :100
Circle: Draw() [Color : White, x : 90, y :70, radius :100
Circle: Draw() [Color : Green, x : 78, y :3, radius :100
Circle: Draw() [Color : Green, x : 64, y :89, radius :100
Circle: Draw() [Color : Blue, x : 3, y :91, radius :100
Circle: Draw() [Color : Blue, x : 62, y :82, radius :100
Circle: Draw() [Color : Green, x : 97, y :61, radius :100
Circle: Draw() [Color : Green, x : 86, y :12, radius :100
Circle: Draw() [Color : Green, x : 38, y :93, radius :100
Circle: Draw() [Color : Red, x : 76, y :82, radius :100
Circle: Draw() [Color : Blue, x : 95, y :82, radius :100

(4)该模式在源码中的体现

享元模式在编辑器系统中大量使用,一个文本编辑器往往会提供很多种字体,而通常的做法就是将每一个字母做成一个享元对象。享元对象的内蕴状态就是 这个字母,而字母在文本中的位置和字体风等其他信息则是外蕴状态,比如字母a可能出现在文本的很多地方,虽然这些字母a的位置和字体风格不同,但是所有这 些地方使用的都是同个字母对象,这样一来,字母对象就可以在整个系统中共享。

在Java语言中,String类型就使用了享元模式.String对象是不变对象,一旦创建出来就不能改变,如果需要改变一个字符串的值,就只 好创建一个新的String对象,在JVM内部, String对象都是共享的。如果一个系统中有两个String对象所包含的字符串相同的话,JVM实际上只创建一个String对象提供给两个引用,从 而实现String对象的共享,String的inern()方法给出这个字符串在共享池中的唯一实例.

(5)享元模式的优缺点

  • 优点:大大减少对象的创建,降低系统的内存,使效率提高。
  • 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。
相关文章
|
23天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
48 24
|
5天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
41 2
|
19天前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
55 5
|
19天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
46 5
|
20天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
21天前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####
|
25天前
|
Java API Maven
商汤人像如何对接?Java代码如何写?
商汤人像如何对接?Java代码如何写?
34 5
|
26天前
|
Java
在Java中实现接口的具体代码示例
可以根据具体的需求,创建更多的类来实现这个接口,以满足不同形状的计算需求。希望这个示例对你理解在 Java 中如何实现接口有所帮助。
41 1
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
36 4
|
18天前
|
安全 Java API
Java中的Lambda表达式:简化代码的现代魔法
在Java 8的发布中,Lambda表达式的引入无疑是一场编程范式的革命。它不仅让代码变得更加简洁,还使得函数式编程在Java中成为可能。本文将深入探讨Lambda表达式如何改变我们编写和维护Java代码的方式,以及它是如何提升我们编码效率的。