上篇博客介绍了单例,简单工厂、工厂模式,
https://blog.youkuaiyun.com/liuguangxu1988/article/details/82053951
这次下篇带来面试提问率相对较高的装饰、策略、观察者模式。同样,简单描述,外加手写个小例子,代码实现,下面介绍,结合了网上的一些资料,仿照了刘望舒大神分享的例子,给大神做个广告,《Android进阶之光》写的不错,推荐大家阅读购买。
一 装饰模式
装饰模式是结构型设计模式之一,其在不必改变类文件和使用继承的情况下,动态扩展一个对象的功能,是继承的替代方案之一。通过创建一个包装对象(也就是装饰),来包裹真实对象。
装饰模式涉及角色
抽象组件:可以是接口或是抽象类
抽象组件具体实现类:被装饰的具体对象
抽象装饰者:扩展抽象组件的功能
抽象装饰者具体实现类:不同的装饰者实现不同的行为
代码举例具体实现
杨过本身会全真剑法,洪七公教了打狗棒法,欧阳锋教了蛤蟆功,洪七公和欧阳峰起到了装饰作用。
1抽象组件:武侠抽象类,抽象攻击方法
public abstract class Swordsman{
public abstract void attack();
}
2抽象组件具体实现类(被装饰的具体对象)
public class YangGuo extends Swordsman{
@override
public void attack(){
//杨过初始武学是全真剑法
System.out.println("杨过使用全真剑法");
}
}
3抽象装饰者(保持了一个对抽象组件的引用,方便调用被装饰对象中的方法)
public abstract class Master extends Swordsman{
private Swordsman mSwordsman;
public Master(Swordsman mSwordsman){
this.mSwordsman = mSwordsman;
}
@override
public void attack(){
mSwordsman.attack();
}
}
4抽象装饰者的具体实现
public class HongQiGong extends Master{
public HongQiGong(Swordsman mSwordsman){
super(mSwordsman);
}
@override
public void attack(){
super.attack();
teachAttack();
}
public void teachAttack(){
System.out.println("洪七公教授打狗棒法");
System.out.println("杨过使用打狗棒法");
}
}
public class OuYangFeng extends Master{
public OuYangFeng(Swordsman mSwordsman){
super(mSwordsman);
}
@override
public void attack(){
super.attack();
teachAttack();
}
public void teachAttack(){
System.out.println("欧阳锋教授蛤蟆功");
System.out.println("杨过使用蛤蟆功");
}
}
5客户端调用
public class Client{
public static void main(String[] args){
YangGuo mYangGuo = new YangGuo();
//洪七公教授打狗棒法,杨过学会了打狗棒法
HongQiGong mHongQiGong = new HongQiGong(mYangGuo);
mHongQiGong.attack();
//欧阳锋教授蛤蟆功,杨过学会了蛤蟆功
OuYangFeng mOuYangFeng = new OuYangFeng(mYangGuo);
mOuYangFeng.attack();
}
}
使用场景
*在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
*这些动态增加的功能可以动态撤销。
*当不能采用继承的方式对系统进行扩充,或者采用继承不利于扩展和维护时。
优点
动态扩展,运行时选择不同的装饰者,实现不同的行为。灵活。
缺点
比继承更加容易出错,排错困难。装饰层次不能过多,否则会影响效率。
二 策略模式
当我们写代码时,有很多选择,衍生if…else…如果一个条件语句中又包含多个条件语句,代码就会变得臃肿。
策略模式:定义一系列算法,把每一个算法封装起来,并且使他们可相互替换。策略模式使得算法可独立于使用它们的客户而独立变化。
设计如下角色
Context : 承上启下,屏蔽高层模块对策略、算法的直接访问。
Stragety : 抽象策略角色,策略、算法的抽象,通常为接口。
ConcreteStragety : 策略的就实现。
举例
张无忌遇到很多对手,每个都用大招,不明智,于是想出3中策略,对付3种实力的对手。
1定义策略接口
public interface FightingStragety{
public void fighting();
}
2分别定义3个策略实现接口
public class WeakrivalStragety implements FightingStragety{
@override
public void fighting(){
System.out.println("弱对手,使用太极剑");
}
}
public class CommonrivalStragety implements FightingStragety{
@override
public void fighting(){
System.out.println("普通对手,使用圣火令");
}
}
public class StrongrivalStragety implements FightingStragety{
@override
public void fighting(){
System.out.println("强大对手,使用乾坤大挪移");
}
}
3上下文角色 通过传进来的不同的具体策略,调用不同的战斗方法
public class Context{
private FightingStragety fightingStragety;
public Context(FightingStragety fightingStragety){
this.fightingStragety = fightingStragety;
}
public void fighting(){
fightingStragety.fighting();
}
}
4客户端调用 举例省略了对实力的判断
public class ZhangWuJi{
public static void main(String[] args){
Context context;
//遇到宋青书,弱对手策略
context = new Context(new WeakrivalStragety());
context.fighting();
//遇到灭绝师太,普通对手策略
context = new Context(new CommonrivalStragety());
context.fighting();
//遇到成昆,强大对手策略
context = new Context(new StrongrivalStragety());
context.fighting();
}
}
使用场景
*对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
*一个类定义了很多行为,这些行为以多个条件语句形式出现。策略模式将相关的条件分支移入他们各自的Stragety类中,以代替这些条件语句。
优点
避免使用多重条件语句,易于扩展。多重条件语句不移维护,易出错。
缺点
每个策略都是一个类,复用性小,策略过多,类的数量过多。
三 观察者模式
又称发布-订阅模式,属于行为设计模式的一种。定义对象间一种一对多的依赖关系,每当一个对象改变状态时,则所有依赖于他的对象都会得到通知并自动更新。
涉及如下
Subject:抽象被观察者,抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
ConcreteSubject:具体被观察者,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
代码的简单实现 举例 韦小宝风流,老婆们看的紧,做什么事之前要报告
1抽象观察者
public interface Observer {
public void update(String message);
}
2具体观察者(七个老婆)
public class GirlFriend implements Observer {
private String name;
public GirlFriend(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "收到消息----" + message);
}
}
3抽象被观察者
public interface Subject {
// 增加订阅者
public void attach(Observer observer);
//删除订阅者
public void detach(Observer observer);
//通知订阅者更新消息
public void notify(String message);
}
4具体被观察者(小宝)
public class WeiXiaoBao implements Subject {
//储存观察者
private List<Observer> list = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
list.add(observer);
}
@Override
public void detach(Observer observer) {
list.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : list) {
observer.update(message);
}
}
}
5客户端调用
public class Client {
public static void main(String[] args) {
WeiXiaoBao mWeiXiaoBao = new WeiXiaoBao ();
//创建女朋友(观察者)
GirlFriend mGirlFriend1 = new GirlFriend("双儿");
GirlFriend mGirlFriend2 = new GirlFriend("建宁");
GirlFriend mGirlFriend3 = new GirlFriend("阿珂");
//观察韦小宝(订阅)
mWeiXiaoBao.attach(mGirlFriend1);
mWeiXiaoBao.attach(mGirlFriend2);
mWeiXiaoBao.attach(mGirlFriend3);
//韦小宝发出消息给订阅的女朋友
mWeiXiaoBao.notify("我去天地会找师傅下棋去了");
}
}
输出结果
双儿 收到消息—-我去天地会找师傅下棋去了
建宁 收到消息—-我去天地会找师傅下棋去了
阿珂 收到消息—-我去天地会找师傅下棋去了
使用场景
*关联行为场景,事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。
优点
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。
缺点
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
总结
这边博客参考了刘望舒大神的 Android进阶之光 中的例子,思路比较清晰,代码示例较为生动,大家可以理解后,具体复杂的实现。如果面试时被问到也可说下,代码不多,手写也没问题,也是会加分的。最后还是码字不易,点个赞呗,哈哈。