今天是软构期末复习的最后一天,虽然我的真题还没有写完,但我还想再写一篇博客。第11章是所有章节中我觉得最难的一章,通过梳理,我发现每一种设计模式都有固定的规律可循,于是我打算做一个整理,浅谈一下我对各个模式的理解。
学习这部分知识时,墙裂建议一边阅读代码,一边画出结构示意图,可以很好的帮助理解,对于设计模式而言,主要掌握框架结构
https://www.runoob.com/design-pattern/design-pattern-intro.html
长文预警!!!
目录
创建型模式
工厂模式
代码范例
总结
结构型模式
适配器模式
代码范例
总结
装饰器模式(重难点!!)
代码范例
总结
行为型模式
策略模式
代码范例
总结
模板模式
代码范例
总结
迭代器模式
代码范例
总结
访问者模式
代码范例
总结
创建型模式
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
代码范例
首先我们先创建目标类的接口,定义目标类的基础操作;然后创建该接口的具体实现类,实现接口中定义的各个操作
在这个代码范例中,Trace为目标实现类的接口;FileTrace和SystemTrace为接口的两个实现类
接着我们就可以实现具体的工厂方法:常见思路是先创建一个工厂接口,定义所有待实现的方法;然后再创建工厂接口的实现类予以实现。这样的两层结构通常可以使得工厂方法的拓展变得更容易一些,同时也可以实现工厂方法的多态【当情景简单的时候,也可以直接定义一个具体的工厂类,实现所有方法即可】
第一种实现
在本例中,TraceFactory是工厂接口;Factory1和Factory2为工厂的具体实现类。可以看见,Factory1和Factory2实现了工厂方法的多态:有参/无参
【此处PPT的代码有一些缺陷,TraceFactory中的otherOperation指的是可补充的其他操作,由于接口性质的限制,当未声明为static/default时方法不能有具体实现;Factory1和Factory2分别省略了接口中其他方法的实现,如果想让代码在编译器上跑通,需自行补充省略的代码】
在这种实现方式下,客户端在使用“工厂方法”创建实例时,必须知道工厂的实现类。必须先实例化出工厂后,才能进一步创建实例
第二种实现(超级巧妙!)
第二种实现在第一种的基础上改进为静态工厂方法:将方法声明为static。结合之前学过的知识我们知道,静态方法可以直接通过“类名.方法名”被调用,无需实例化方法所在的类,可以减少内存消耗。
总结
工厂方法隐藏了目标类的具体实现类,实现了信息隐藏的目标。如使用:“工厂接口-具体实现类”的结构,可使代码易于扩展;如使用静态的工厂方法,使用时可不实例化工厂,节省内存。
结构型模式
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
优点: 1、可以让任何两个没有关联的类一起运行。
2、提高了类的复用。
3、增加了类的透明度。
4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
2.由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
使用场景:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
代码范例
假设客户端想使用一个功能,而目前手头恰有一个类可以实现这个功能,但是两者的接口不一致,比如体现为:参数不同。此时我们就可以引入适配器进行适配,将客户端输入进行处理,使其符合已有方法的参数要求。如此可以提高代码的可复用性,一次开发,多次使用。
总结
适配器用于沟通客户端与已有功能实现类,通过适配,提高已有代码可复用性
装饰器模式(重难点!!)
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。【当然也可以减少功能,如unmodifiablexxx的实现:注释掉mutator的实现代码】
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
何时使用:在不想增加很多子类的情况下扩展类。
如何解决:将具体功能职责划分,同时继承装饰者模式。
关键代码: 1、Component 类充当抽象角色,不应该具体实现。
2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
代码范例 1
本例建立于实现多种功能Stack的情境之下,使用装饰器通过委托的方式,相较于通过继承,可以减少代码重复
首先先创建被装饰类的接口Stack,定义该数据类型的基础操作;并在ArrayStack中实现【截止目前的设计思路等同于普通的“接口”→“实现类”】,ArrayStack中定义了Stack的基础属性,是最基本的Stack实现类(其他类可以此为基础实现)
接着创建与被装饰类相对应的装饰器StackDecorator,装饰器是继承了Stack接口的抽象类。注意到,在装饰器中新增了一个protected属性,类型为Stack,用于接收传入的装饰参数,装饰器可以通过传入的装饰参数的特性进行装饰,故而在实现Stack接口的方法时,全都委托传入的装饰参数进行实现——如此就可以获得装饰参数的特性,完成装饰
接下来实现具体的装饰器类 ,此处以UndoStack为例。具体的装饰器类同时继承了Stack接口与装饰器StackDecorator,同时添加了自己所需新属性与新方法。在重写Stack接口的方法时,用super()继承父类方法,此外也能添加一些新内容
客户端实际使用方法如下:装饰器的使用就像是一层层穿衣服
总结
行为型模式
策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
代码范例
首先创建策略接口Strategy,定义待实现的目标方法【希望可变的算法】
接着创建接口的实现类,实现目标方法的不同的算法 【可根据需要添加自己特有的属性以及相对应构造方法】
然后创建行为随着策略对象改变而改变的 context 对象,在这个过程中使用了委托的方式,具体实现有很多种【参考委托的分类:依赖,联系,组合/聚合】
客户端使用如下:
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!