Java的设计模式

Java的设计模式

良好的OO设计必须具备可复用,可扩充,可维护三个特性。

- 多用组合,少用继承(“有一个”可能比“是一个”更好)

- 针对接口编程,不针对实现编程(针对接口编程的真正意思是“针对超类型编程” 也就是多态)

- 为交互对象之间的松耦合设计而努力

- 封装变化(将变化封装起来)

- 对扩展开放,对修改关闭

- 最少知道原则----“只和朋友交谈”

- 别找我,我会来找你

- 类应该只有一个改变的理由

- 依赖倒置原则(要依赖抽象,不要依赖具体类)

1. 策略模式

设计原则:

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码会在一起。 (把会变化的部分取出并封装起来,以便以后可以轻易的改动或扩充此部分,而不影响不需要变化的其他部分)

  1. 将不变的功能放在同一个父类中,将需要改变的功能单独组成一个接口,使用者只需要继承一个父类,里面装有其他功能的对象—>(接口的引用表示)。

例如:
设计模式中的模拟鸭子

2. 观察者模式

定义: 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

  1. 可观察者用一个共同的接口来更新观察者。
  2. 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
  3. 有多个观察者时,不可以依赖特定的通知次序。

例如:
设计模式中的气象站
Java的util包中包含的Observer接口和Observable类
swing和GUI框架

3. 装饰者模式

定义: 动态地将责任附加到对象上。 若要扩展功能,装饰着提供了比继承者更有弹性的替代方案。 缺点:可能会在设计中加入大量的小类。

  1. 装饰者和被装饰者必须继承同一个超类或者实现同一个接口(最好)否则无法装饰。
  2. 可以用一个或多个装饰者包装一个对象。
  3. 被装饰者需要放在装饰者的类里面,并且在同样的方法里对装饰者的方法进行“装饰”。
  4. 使用继承的话,耦合过高,弹性小,维护成本大。而通过动态地组合对象,可以写新的代码添加新功能,而无需修改现有的代码,可以再运行时动态地加上新行为(因为多态)。

例如:
设计模式中的星巴兹咖啡
Java的IO类
new LineNumberInputStream(new BufferedInputStream(new FileInputStream(文件名)))

4. 工厂模式

定义:
1.抽象工厂模式:(组合) 提供一个接口,用于创建相关或依赖对象的“家族”,而不需要明确指定具体类。它的每一个方法都像是工厂方法,接口中的每一个方法都负责创建一个产品。
要想使用这个工厂,需先实例化它,然后将它传入一些针对抽象类型所写的代码中。

2.工厂方法模式:(继承) 定义了一个创建对象的接口,但由子类来决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类(工厂方法是抽象的,所以依赖子类来处理对象的创建)。
工厂方法必须返回一个产品,超类中定义的方法,通常使用到工厂方法的返回值。 工厂方法可能需要参数来指定所要生产的具体产品。
这个抽象类的其他方法(父类已经实现的方法)都可能需要工厂方法提供的对象实例(由子类创建)。

  1. 工厂用于处理创建对象的细节。
  2. 一个工厂可以被不同的顾客拿来使用。
  3. 利用静态方法定义一个简单的工厂成为静态工厂,它可以不需要使用创建对象的方法来实例化对象,不过不能通过继承来改变创建对象的方法。
  4. 当你直接new实例化一个对象时,就是在依赖它的具体类。
  5. 依赖倒置:不能让高层组件依赖低层组件,两者都应该依赖于抽象。
  6. 变量不可以持有具体类的引用(要是抽象类)。
  7. 不要让类派生自具体类。(也得是接口)。
  8. 不要覆盖基类已经实现的方法(基类实现的方法应该由子类共享)。

例如:
设计模式中的披萨工厂

5.单例模式

定义: 确保一个类只有一个实例,并提供全局访问点。

  1. 单例类的构造器为私有,另外拥有一个静态变量对象的引用和一个静态返回单例对象的方法。
  2. 当你需要一个实例时,向单例类查询,它会返回单个实例。

在多线程的情况下:

  1. syschorized(需要有volatile修饰实例引用,当实例完后别的线程会立刻知道) : 性能会下降
  2. 静态实例对象: 类初始化的时候直接实例化一个静态对象
  3. 加锁lock:在同步的情况下,对性能影响不大,仅在第一次实例的时候进行加锁

注意:
如果有多个类加载器加载一个单例类,会出现多个对象,所以尽量避免。

例如:
用来管理共享资源,数据库连接池,线程池。

6.适配器模式

定义: 将一个类的接口,转换成客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。
使用对象组合,以修改的接口包装被适配者。被适配者的任何子类,都可以搭配着适配器使用。

  1. 类适配器需要使用多继承,java中不允许这样。
  2. 对象适配器使用组合的方式,不仅可以适配某个类,还可以适配某类的任何子类。
  3. 适配器的实现难度与目标接口的大小和复杂度相关。

例如:
设计模式中的枚举器与迭代器之间的适配。

7.外观模式

定义: 提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层的接口,让子系统更容易使用。

  1. 通过实现一个提供更合理的接口的外观类,可以将一个复杂的子系统变得容易使用。
  2. 外观没有封装子系统的类,只是提供了简化的接口,理论上你还可以使用复杂的子系统类。
  3. 不只简化了接口,也将客户从组件的子系统中解耦。
  4. 不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他部分。
  5. 最少知道法则:如果某对象是调用其他方法返回的结果,就不要调用该对象的方法!P266
  6. 要在抽象与速度之间取舍,在空间和时间之间平衡。

例如:
设计模式中的构造家庭影院。

8.迭代器模式

定义: 提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

  1. 用来封装“遍历集合内的每个对象的过程”。
  2. 集合 = 聚合
  3. java.util.Iterator接口,hasNext(); next(); remove()
  4. 如果不想使用上面的remove()方法,可以抛出一个异常。
  5. 当程序在多线程的情况下使用迭代器的remove()方法时,必须特别小心。
  6. 迭代器意味着没有次序,不可以对迭代器索取出的元素大小顺序作出假设。
  7. java还提供一种ListIterator接口,可以取得前一个元素。方法是previous()
  8. HashTable对迭代器的支持是间接的,要先取出value再取得迭代器。

例如:
设计模式中的餐厅菜单合并。

9.组合模式

定义: 允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理对象以及对象组合。
组合包含组件。组件有两种:组合与叶节点元素。

  1. 组合模式以单一责任设计原则换取透明性。
  2. 为了要保持透明性,组合内所有的对象都必须实现相同的接口。
  3. 如果这个组合结构很复杂,或者遍历的代价太高,那么实现组合节点的缓存就很有帮助。

例如:
设计模式中的餐厅菜单的子菜单。

10.状态模式

定义: 允许对象在内部状态改变时改变它的行为。对象看起来好像修改了它的类。
第一部分的意思是这个模式将状态封装成独立的类,并将动作委托到代表当前状态的对象,我们知道行为会随内部状态而改变。
第二部分实际上是在使用组合通过简单引用不同的状态对象来造成类改变的假象。

  1. 状态使用在Context中代表它的内部状态以及行为的,所以只有Context才会对它的内部状态提出请求。客户不会直接改变Context状态。
  2. 如果有多个Context状态想要共享实例,需要把每个状态都指定到静态的实例变量中。
  3. 当Context对象创建时,可以设置它的初始状态,之后他们会随着时间自己改变状态。
  4. 任何的状态改变都是定义好的,“改变”状态这件事建立在自己的方案中。
  5. 策略模式通常会用行为或算法来配置Context类,状态模式允许Context随着状态的改变而改变行为。

例如:
设计模式中的糖果机。

11.代理模式

定义: 为另一个对象提供一个替身或占位符以访问这个对象。(使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象,创建开销大的对象或者需要安全控制的对象)
RMI:提供了客户辅助对象和服务辅助对象,为客户辅助对象创建和服务对象相同的方法。
制作远程服务的五个步骤:1.制作远程接口 2.制作远程实现 3.利用rmic产生的stub和skeleton 4.启动RMI的registry 5.开启远程服务
(Java5的RMI和动态代理搭配使用,动态代理动态产生stub,远程对象的stub是Java.lang.reflect.Proxy实例,它是自动产生的,来处理所有把客户的本地调用变成远程调用的细节)

  1. 远程代理:远程代理可以作为另一个JVM上对象的本地代表。调用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再有代理将结果转给客户。
  2. 虚拟代理:虚拟代理作为创建开销大的对象的代表。虚拟代理经常知道我们需要一个真正的对象时才创建它。当对象在创建前和创建中时,由虚拟代表来扮演对象的替身。对象创建后,代理会将请求直接委托给对象。
  3. 保护代理:根据访问权限决定客户可否访问对象的代理。
  4. 动态代理:利用Java.lang.reflect包在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。
  5. 防火墙代理:控制网络资源的访问,保护主题免于“坏客户”的侵害。
  6. 智能引用代理:当主题被引用时,进行额外的动作(例如计算一个对象被引用的次数)。
  7. 缓存代理:为开销大的运算结果提供暂时存储,它允许多个客户共享结果,以减少计算或网络延迟。
  8. 同步代理:在多线程情况下为主题提供安全的访问。
  9. 复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度,有时也称为外观代理。
  10. 写入时复制代理:用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。

代理都会将客户对主题施加的方法调用拦截下来。

例如:
设计模式中的糖果机监视器。
Web服务器代理。

12.复合模式之MVC

定义: 复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的"一般性"问题。

1.视图:用来呈现模型。视图通常直接从模型中取得它需要显示的状态和数据。
2.控制器:取得用户的输入并解读其对模型的意思(也就是将视图的动作转化成模型上的动作,其中他要决定调用哪个模型上的哪个方法)。
3.模型:模型持有所有的数据,状态和程序逻辑。模型没有注意到视图和控制器,虽然它提供了操纵和检索状态的接口,并发送状态改变通知给观察者。

模型利用观察者让控制器和视图可以随最新的状态改变而更新。视图和控制器则实现策略模式,其中控制器属于策略,想更换行为可以换一个控制器。视图内部有时会使用到组合模式(GUI,Swing)。

  1. 视图是模型的窗口。当你对视图做一些事情时,视图就告诉控制器你做了什么。控制器会负责处理。
  2. 控制器解读你的动作。并告知模型做出对应的处理。(控制器把控制逻辑从视图中分离,让模型和视图之间解耦)
  3. 当控制器接收到视图的某一动作时,结果可能是它也需要告诉视图改变其结果。
  4. 当模型状态改变时,模型会通知视图。
  5. 视图可以直接从模型中取得它显示的状态。
  6. 控制器也可以变成模型的观察者。在某些设计中,控制器会向模型注册,模型一有改变,就通知控制器。
  7. 通常情况下,运行时一个试图搭配一个控制器,但是一个控制器也可以管理多个视图。

例如:
MVC与Web 设计模式P549(当模型改变时,视图间接地从控制器收到了相当于通知的东西。控制器甚至把包装成Bean的模型状态送给视图,允许视图可以用Bean属性取得模型的状态)

其他模式

命令模式:
将请求封装成对象,这可以让你使用不同的请求,队列,或者日志请求来参数化其他对象。命令模式也可以支持撤销操作。

模板方法模式:
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

桥接模式:
使用桥接模式不只改变你的实现,也改变你的抽象。

生成器模式:
使用生成器模式封装一个产品的构造过程,并允许步骤构造。

责任链模式:
当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式。

中介者模式:
使用中介者模式来集中相关对象之间复杂的沟通和控制方式。

原型模式:
当创建给定类的实例的过程和昂贵或很复杂时,就使用原型模式。

推荐一篇好博文https://blog.csdn.net/charon_chui/article/details/80538949

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值