(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 接口的实体类 RedCircle、GreenCircle。Shape 是一个抽象类,将使用 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); } }
使用 Shape 和 DrawAPI 类画出不同颜色的圆。
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)享元模式的优缺点
- 优点:大大减少对象的创建,降低系统的内存,使效率提高。
- 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。