设计模式——结构型模式(适配器,桥接,过滤器,组合,装饰器,外观,享元,代理)(1)https://developer.aliyun.com/article/1551847
3、举例
JUnit是一个单元测试框架,按照此框架的规范编写测试代码可以使单元测试自动化。为达到自动化的目的,JUnit定义了两个概念:TestCase和 TestSuite。前者是编写的测试类,后者是一个不同 TestCase的集合,当然这个集合里可以包含 TestSuite元素,这样运行一个 TestSuite会将其包含的 TestCase全部运行
但是在真实运行测试程序时不需要关心这个类是 TestCase还是 TestSuite,只关心测试运行结果如何。这就是 JUnit使用组合模式的原因
JUnit为了采用组合模式将 TestCase和 TestSuite统一起来,创建了一个 Test接口来扮演抽象构件角色,这样原来的 TestCase扮演组合模式中的树叶构件角色,而 TestSuite扮演组合模式中的树枝构件角色
//Test接口——抽象构件角色 interface Test { /** * Counts the number of test cases that will be run by this test. */ public abstract int countTestCase(); /** * Runs a test and collects its result in a TestResult instance. */ public abstract void run(TestResult result); } //TestSuite类的部分有关源码——Composite角色,实现了接口 Test class TestSuite implements Test { //使用较老的 Vector保存添加的 test private Vector fTests = new Vector(10); private String fName; /** * Adds a test to the suite. */ public void addTest(Test test) { //这里的参数是 Test类型的,意味着 TestCase和 TestSuite以及以后实现 Test接口的任何类都可以被添加进来 fTests.addElement(test); } /** * Counts the number of test cases that will be run by this test. */ public int countTestCase() { int count= 0; for (Enumeration e= tests(); e.hasMoreElements(); ) { Test test= (Test)e.nextElement(); count= count + test.countTestCases(); } return count; } /** * Runs the tests and collects their result in a TestResult. */ public void run(TestResult result) { for (Enumeration e= tests(); e.hasMoreElements(); ) { if (result.shouldStop() ) break; Test test= (Test)e.nextElement(); //关键在这个方法 runTest(test, result); } } //该方法中是递归的调用,至于 Test是什么类型只有运行时得知 public void runTest(Test test, TestResult result) { test.run(result); } } //TestCase类的部分有关源码——Leaf角色,编写的测试类就是继承于它 abstract class TestCase extends Assert implements Test { /** * Counts the number of test cases executed by run(TestResult result). */ public int countTestCases() { return 1; } /** * Runs the test case and collects the results in TestResult. */ public void run(TestResult result) { result.run(this); } }
五、装饰器模式
1、楔子
“装饰”一词,肯定让你想到了又黑又火的家庭装修。两者在道理上有很多相似之处。家庭装修就是在实而不华的墙面上涂抹一层华而不实的颜料,看起来多姿多彩。但是墙仍旧是墙,本质并没有发生变化
2、解析
装饰器模式动态地给一个对象添加一些额外的功能。添加的方式是对客户透明的,因此客户端并不会觉得对象在装饰前和装饰后有什么不同。这样装饰器模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展
模式组成:
- 抽象构件角色:定义一个抽象接口,规范准备接受附加功能的对象
- 具体构件角色:被装饰者,定义一个将要被装饰增加功能的类
- 装饰角色:持有一个构件角色的实例,并定义了抽象构件定义的接口
- 具体装饰角色:给构件增加功能
一般有两种方式可以实现给一个类或对象增加行为:
- 继承机制(通过继承一个现有类可以使得子类在拥有自身方法的同时还拥有父类的方法)
- 关联机制(将一个类的对象嵌入另一个对象中,由另一个对象决定是否调用嵌入对象的行为以便扩展自己的行为)
与继承关系相比,关联关系的优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。使用装饰器模式来实现扩展比继承更加灵活,可以在不需要创造更多子类的情况下,将对象的功能加以扩展
组合模式侧重通过递归组合构造类,使不同的对象、多重的对象可以“一视同仁”;装饰器模式仅仅是借递归组合来达到定义中的目的
3、举例
JUnit中 TestCase是一个很重要的类,允许对其进行进行功能扩展
在 junit.extensions包中,TestDecorator、RepeatedTest就是对 TestCase的装饰器模式扩展
//抽象构件角色 interface Test { /** * Counts the number of test cases that will be run by this test. */ public abstract int countTestCases(); /** * Runs a test and collects its result in a TestResult instance. */ public abstract void run(TestResult result); } //具体构件对象,这里是抽象类 abstract class TestCase extends Assert implements Test { public int countTestCases() { return 1; } public TestResult run() { TestResult result = createResult(); run(result); return result; } public void run(TestResult result) { result.run(this); } } //装饰角色 class TestDecorator extends Assert implements Test { //按照上面的要求保留了一个对构件对象的实例 protected Test fTest; public TestDecorator(Test test) { fTest = test; } /** * The basic run behaviour. */ public void basicRun(TestResult result) { fTest.run(result); } public int countTestCases() { return fTest.countTestCases(); } public void run(TestResult result) { basicRun(result); } public String toString() { return fTest.toString(); } public Test getTest() { return fTest; } } //具体装饰角色。该类的作用是设置测试类的执行次数 class RepeatedTest extends TestDecorator { private int fTimesRepeat; public RepeatedTest(Test test, int repeat) { super(test); if (repeat < 0) throw new IllegalArgumentException("Repetition count must be > 0"); fTimesRepeat = repeat; } //如何进行装饰 public int countTestCases() { return super.countTestCases()*fTimesRepeat; } public void run(TestResult result) { for (int i= 0; i < fTimesRepeat; i+ + ) { if (result.shouldStop()) break; super.run(result); } } public String toString() { return super.toString() + "(repeated)"; } } /* 使用时,采用如下方式: TestDecorator test = new RepeatedTest(new TestXXX(), 3); */
六、外观模式
1、解析
外观模式:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面
模式组成:
- 外观角色:外观模式的核心。被用户角色调用,因此熟悉子系统的功能。其内部根据客户角色已有的需求预定了几种功能组合
- 子系统角色:实现子系统的功能。对它而言,外观角色和客户角色是未知的,它没有任何外观角色的信息和链接
- 客户角色:调用外观角色完成要得到的功能
根据“单一职责原则”,在软件中将一个系统划分为若干个子系统有利于降低整个系统的复杂性。常见的设计目标是使子系统间的通信和相互依赖关系达到最小,而达到该目标的途径之一就是引入一个外观对象,它为子系统的访问提供了一个简单而单一的入口
外观模式也是“迪米特法则”的体现,通过引入一个新的外观类可以降低原有系统的复杂度,同时降低客户类与子系统类的耦合度
外观模式要求一个子系统的外部与其内部的通信通过一个统一的外观对象进行,外观类将客户端与子系统的内部复杂性分隔开,使得客户端只需要与外观对象打交道,而不需要与子系统内部的很多对象打交道
外观模式适用场景:
- 为一个复杂子系统提供一个简单接口
- 客户程序与抽象类的实现部分之间存在很大的依赖性
- 需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点
优点:
- 对客户屏蔽子系统组件,减少了客户处理的对象的数目,使子系统使用更加方便
- 实现了子系统与客户之间的松耦合关系,而子系统内部的功能组件往往是紧耦合的
2、举例
典型应用是进行 数据库连接
一般每次对数据库进行访问时都要执行如下操作:先得到 connect实例,然后打开 connect获得链接,得到一个 statement,执行 sql语句进行查询,得到查询结果集
将步骤提取出来并封装到一个类中,这样每次执行数据库访问时只需要将必要的参数传递到类中即可
七、享元模式
1、楔子
面向对象技术可以很好地解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致运行代价过高,带来性能下降等问题
享元模式正是为解决这一类问题而诞生的,它通过共享技术实现相同或相似对象的重用
2、解析
享元模式:运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用
在享元模式中可以共享的相同内容称为内部状态,需要外部环境来设置的不能共享的内容称为外部状态。可以通过设置不同的外部状态使得相同的对象具有不同的特征,而相同的内部状态是可以共享的
在享元模式中通常会出现工厂模式,需要创建一个享元工厂来负责维护一个享元池,用于存储具有相同内部状态的享元对象
在实际使用中,能够共享的内部状态是有限的,因此享元对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为细粒度对象
享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的 设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式
优点:大大减少对象的创建,降低系统的内存,使效率提高
缺点:
- 提高了系统的复杂度
- 需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
享元模式试用场景:
- 系统有大量相似对象
- 需要缓冲池的场景
3、举例
import java.util.HashMap; import java.util.Map; public class Test { public static void main(String args[]){ String yundong ="足球"; for(int i=0;i<5;i++){ TiYuGuan tiYuGuan = JianZhuFactory.getTyg(yundong); tiYuGuan.setName("合肥体育馆"); tiYuGuan.setShape("椭圆形"); tiYuGuan.setYongtu("比赛"); tiYuGuan.use(); } } } //定义一个建筑接口 interface Jianzhu{ void use(); } //创建一个体育馆 class TiYuGuan implements Jianzhu{ private String name; private String shape; private String yongtu; private String yundong; public TiYuGuan(String yundong) { this.yundong = yundong; } public String getYongtu() { return yongtu; } public void setYongtu(String yongtu) { this.yongtu = yongtu; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getShape() { return shape; } public void setShape(String shape) { this.shape = shape; } public String getYundong() { return yundong; } public void setYundong(String yundong) { this.yundong = yundong; } public void use() { System.out.println("该体育馆被使用于"+yongtu+",项目为:"+ yundong+",场地形状为:"+shape+",场地名称为:"+name+",对象:"+this); } } //需要用到工厂类 建筑工厂 可以产出体育馆等建筑 class JianZhuFactory{ private static final Map<String,TiYuGuan> tygs =new HashMap<String,TiYuGuan>(); public static TiYuGuan getTyg(String yundong){ TiYuGuan tyg = tygs.get(yundong); if(tyg==null){ tygs.put(yundong,new TiYuGuan(yundong)); } return tygs.get(yundong); } public static int getSize(){ return tygs.size(); } }
八、代理模式
1、楔子
在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务
2、解析
代理模式为其他对象提供一种代理以控制对这个对象的访问
模式组成:
- 抽象主题角色:声明真实主题和代理主题的共同接口
- 代理主题角色:内部包含对真实主题的引用,并且提供和真实主题角色相同的接口
- 真实主题角色:定义真实的对象
代理模式分为8种,这里介绍常见的几个:
- 远程代理:为一个位于不同地址空间的对象提供一个局域代表对象
- 虚拟代理:使用一个资源消耗很大或者比较复杂的对象产生很大延迟时才创建
- 保护代理:控制对一个对象的访问权限
- 智能引用代理:提供比对目标对象额外的服务。乳记录访问的流量,提供友情提示等
3、举例
以论坛为例。注册用户拥有发帖,修改个人信息,修改帖子等功能;游客只能看别人发的帖子,没有其他权限。为了简化代码,这里只实现发帖权限的控制
首先实现一个抽象主题角色 MyForum,其中定义了真实主题和代理主题的共同接口——发帖功能
public interface MyForum { public void AddFile(); }
真实主题角色和代理主题角色都要实现该接口,前者基本是将这个接口的方法内容填充起来,这里不再赘述,主要实现代理主题角色
public class MyForumProxy implements MyForum { private RealMyForum forum = new RealMyForum() ; private int permission ; //权限值 public MyForumProxy(int permission) { this.permission = permission ; } //实现的接口 public void AddFile() { //满足权限设置时才执行操作 //Constants是一个常量类 if(Constants.ASSOCIATOR = = permission) { forum.AddFile(); } else System.out.println("You are not a associator of MyForum, please registe!"); } }