前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第24次文章:结构性模式(续)

第24次文章:结构性模式(续)

作者头像
鹏-程-万-里
发布2019-09-28 17:56:31
4320
发布2019-09-28 17:56:31
举报
文章被收录于专栏:Java小白成长之路

本周把结构性模式的剩余几种模式学完了,并且开始接触行为型模式。为便于对文章的分类,本周的文章就只把结构性模式全部扫尾处理。行为型模式放在下周的文章中一起介绍吧!


一、装饰器模式(decorator)

1、职责

(1)动态的为一个对象增加新的功能

(2)装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。

2、实现细节

(1)Component抽象构件角色

真实对象和装饰对象有相同的接口,这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。

(2)ConcreteComponent具体构建角色

需要被装饰的真实对象。

(3)Decorator装饰角色

持有一个抽象构件的引用,装饰对象接收所有客户端的请求,并把这些请求转发给真实对象,这样,就能在真实对象调用前后增加新的功能。

(4)ConcreteDecorator具体装饰角色

负责给构件对象增加新的责任。

3、具体实现

首先我们可以模拟一个场景,一辆普通的汽车,我们想要在这辆车上增加一系列的新功能。此时我们尝试使用装饰器模式,那么,首先我们需要创建一个抽象构件汽车类,然后对于具体构建角色,我们可以设置为一辆普通的陆地上跑的车。想要对其增加新的功能,就类似于对这辆普通车进行装饰。需要构建一个Decorator角色,然后衍生出一些列具有新功能的具体车。下面我们使用代码进行尝试!

(1)四个实现细节

代码语言:javascript
复制
//Component抽象构件角色public interface ICar {  void move();}
//ConcreteComponent具体构建角色(真实对象)class Car implements ICar{  @Override  public void move() {    System.out.println("陆地上跑!");    }  }
//Decorator装饰角色class SuperCar implements ICar{  @Override  public void move() {  }}
//ConcreteDecorator具体装饰角色class FlyCar extends SuperCar{  private ICar car;  public FlyCar(ICar car) {    super();    this.car = car;  }  @Override  public void move() {    car.move();    System.out.println("天上飞!");  }}
//ConcreteDecorator具体装饰角色class WaterCar extends SuperCar{  private ICar car;  public WaterCar(ICar car) {    super();    this.car = car;  }  @Override  public void move() {    car.move();    System.out.println("水上漂!");  }}
//ConcreteDecorator具体装饰角色class AutoCar extends SuperCar{  private ICar car;  public AutoCar(ICar car) {    super();    this.car = car;  }  @Override  public void move() {    car.move();    System.out.println("自动驾驶!");  }}

经过上述的实现,每个类之间的相互关系如下所示:

tips:我们按照装饰模式的实现细节,依次实现各个构件。对于Decorator装饰角色的构建,我们可以在Decorator中自行添加其他的控制信息和方法。

(2)简单测试装饰模式

代码语言:javascript
复制
public class Client {  public static void main(String[] args) {    Car car = new Car();    car.move();            System.out.println("############加入飞行功能############");    ICar flyCar = new FlyCar(car);    flyCar.move();    System.out.println("############加入水上漂功能############");    WaterCar waterCar = new WaterCar(car);    waterCar.move();    System.out.println("############功能复用所有功能############");    AutoCar autoCar = new AutoCar(new WaterCar(new FlyCar(new Car())));    autoCar.move();  }}

查看一下结果图:

tips:

在测试代码中,我们可以看出来,对于不同功能的叠加,只需要向ConcreteDecorator具体装饰角色中传入需要的基本功能的基础车就可以了。并且可以通多多次new不同的具体装饰角色,使得最后得到的产品具有各种具体装饰角色的装饰功能。

4、开发中使用的场景

(1)IO中输入流和输出流的设计

(2)Swing包中图形界面构建功能

(3)Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper类,增强了request对象的功能。

(4)Struts2中,request,response,session对象的处理

5、总结

(1)装饰模式也叫包装器模式

(2)装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以随意变化,以便增加新的具体构建类和具体装饰类。

(3)优点

-扩展对象功能,比继承灵活,不会导致类个数急剧增加

-可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象

-具体构建类和具体装饰类可以独立变化,用户可以根据需要,自己增加新的具体构建子类和具体装饰子类。

(4)缺点

-产生很多小对象。大量小对象占据内存,一定程度上影响性能。

-装饰模式易于出错,调试排查比较麻烦。

6、装饰模式和桥接模式的区别

两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能,但是既有部分是很稳定的。

二、外观模式

1、迪米特法则(最少知识原则)

一个软件实体应当大部分尽可能少的与其他实体发生相互作用

2、外观模式核心

为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。

3、开发中常见的场景

频率很高。哪里都会遇到。各种技术和框架中,都有外观模式的使用。

4、总结

对于外观模式,只要是学过java的同学,无论是否知道这个模式,都一定会使用这个模式。其实简单讲,就是自己对一些功能进行封装处理,使得客户端在调用这些功能的时候不需要知道内部的具体是如何实现的,只用知道如何调用,传递哪些参数就好了!所以我们在此处不过多的赘述,也不放入相应代码了!

三、享元模式(FlyWeight)

1、场景

内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存。

2、核心

(1)享元模式以共享的方式高效的支持大量细粒度对象的重用。

(2)享元模式能做到共享的关键是区分内部状态和外部状态

-内部状态:可以共享,不会随环境变化而改变

-外部状态:不可以共享,会随环境变化而改变

3、享元模式实现细节

(1)FlyWeightFactory享元工厂类

创建并管理享元对象,享元池一般设计成键值对

(2)FlyWeight抽象享元类

通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态

(3)ConcreteFlyWeight具体享元类

为内部状态提供成员变量进行存储

(4)UnsharedConcreteFlyWeight非共享享元类

不能被共享的子类可以设计为非共享享元类

4、代码实现

对于享元模式我们使用围棋的棋子来进行模拟。在围棋棋子中,只有黑色和白色两种棋子,每一种颜色的棋子除了放在棋盘上的位置不同之外,其他的各个属性都完全相同。所以在这种情况下,我们可以将棋子当做共享的元素,显然,整个棋盘中就只有两个对象:黑棋和白棋。对于这两个类中每个对象而言,大小,颜色,等等其他属性都是完全相同的,不同的只有位置。为简化我们的代码实现,我们将共享的内部状态就设为颜色(不考虑其他属性了),外部状态设为位置。

(1)对内部状态进行封装

代码语言:javascript
复制
//抽象享元类public interface ChessFlyWeight {  String getColor();  void display(Coordinate c);}
//ConcreteFlyWeight具体享元类class ChessConcreteFlyWeight implements ChessFlyWeight{  private String color;  public ChessConcreteFlyWeight(String color) {    super();    this.color = color;  }  @Override  public String getColor() {    return this.color;  }  @Override  public void display(Coordinate c) {    System.out.println("棋子的颜色:"+this.color);    System.out.println("棋子的位置:"+c.getX()+"-----"+c.getY());  }}

(2)外部状态:棋子坐标(UnsharedConcreteFlyWeight非共享享元类)

代码语言:javascript
复制
public class Coordinate {  private int x,y;  public Coordinate(int x, int y) {    super();    this.x = x;    this.y = y;  }  public int getX() {    return x;  }  public void setX(int x) {    this.x = x;  }  public int getY() {    return y;  }  public void setY(int y) {    this.y = y;  }}

(3)享元工厂:使用map键值对进行存储和生产新对象

代码语言:javascript
复制
public class FlyWeightFactory {  //享元池,存放共享元素  public static Map<String,ChessConcreteFlyWeight> map = new HashMap<String,ChessConcreteFlyWeight>();  public static ChessConcreteFlyWeight getChess(String color) {    if (map.get(color)!=null) {      return map.get(color);    }else {      ChessConcreteFlyWeight ccfw = new ChessConcreteFlyWeight(color);      map.put(color, ccfw);      return ccfw;    }  }}

(4)简单测试

代码语言:javascript
复制
public class Client {  public static void main(String[] args) {    ChessFlyWeight cfw1 = FlyWeightFactory.getChess("black");    ChessFlyWeight cfw2 = FlyWeightFactory.getChess("black");    System.out.println(cfw1);    System.out.println(cfw2);        System.out.println("增加外部状态处理");    cfw1.display(new Coordinate(10,10));    cfw2.display(new Coordinate(20,20));  }}

上述代码的类关系图如下所示:

测试结果:

tips:

在享元工厂的构建中,我们使用颜色作为键,只有颜色不同时我们才会产生新的对象。所以,如果两个对象的颜色相同,那么两者其实属于同一个对象。在测试代码中我们分别打印了两个颜色相同的对象cfw1和cfw2,在最后的控制台输出上(红色方框)可以看出,两者属于是同一个对象。这就代表了享元模式的核心思想:共享元素,减少内存消耗。

5、优缺点

(1)优点

-极大减少内存中对象的数量

-相同或相似对象内存中只存一份,极大的节约资源,提高系统性能

-外部状态相对独立,不影响内部状态

(2)缺点

-模式较复杂,使得程序逻辑复杂化

-为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。用时间换取了空间。

6、应用场景

(1)享元模式由于其共享的特性,可以在任何“池”中操作。比如:线程池,数据库连接池。

(2)String类的设计也是享元模式

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java小白成长之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、装饰器模式(decorator)
    • 1、职责
      • 2、实现细节
        • 3、具体实现
          • 4、开发中使用的场景
            • 5、总结
              • 6、装饰模式和桥接模式的区别
              • 二、外观模式
                • 1、迪米特法则(最少知识原则)
                  • 2、外观模式核心
                    • 3、开发中常见的场景
                      • 4、总结
                      • 三、享元模式(FlyWeight)
                        • 1、场景
                          • 2、核心
                            • 3、享元模式实现细节
                              • 4、代码实现
                                • 5、优缺点
                                  • 6、应用场景
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档