最近写代码没有手感,就看看书找点写代码的灵感。看点高大上的设计模式谈谈自己的理解。我读的是《研磨设计模式》看完之后真正的醍醐灌顶。借用一句话
一本值得反复研读的书
读了设计模式,其实我觉得看Android源码不是一行一行的看,毕竟源码的代码太多,其实很多都是输入检查非空判断异常处理等,如果只是关心的关键的实现步骤抓重点的话,就找到关键实现就好了。这话说得有点绕。我们怎么知道什么是重点。这就个人代码功力还有就是设计模式的理解了。
个人功力就靠平常的开发经验积累思维拓展。但是设计模式就可以看书学习啊。这里强行安利,《研磨设计模式》真的神书看完醍醐灌顶。《Android源码设计模式解析与实战》个人觉得不如前者但是可以作为拓展,因为结合Android源码分析更实际。
我在我的博客上-源码分析-ListView组件addHeaderView()方法的源码解析上分析了ListView的实现addHeadView的源码。当时没有看到装饰者模式。只知道Google在对添加headview上包装了一个类,而不是继承一个类。觉得这样写很好实现代码解耦。后来看完《研磨设计模式》的装饰者模式篇才明白为什么这写。
装饰者模式的定义:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰者模式比生成子类更灵活。
考虑到实际的代码中。比如我们需要给listview添加一个能够添加头部视图headView的功能。这就是需要动态地添加一些额外的职责。而且为了解耦和不影响现有代码,最好是透明的。所谓透明就是给一个对象增加功能,但是不能让这个对象知道,也就是不能去改动这个对象。
从代码的角度看,给对象增加功能,就是拓展对象功能,就是继承啊。这是网上大多数对RecyclerView添加headview时候的真实考虑和代码逻辑实现。但是不透明。我们看到Android源码ListView的addHeadView实现不是这样的。因为继承是非常不灵活的复用方式。这里就需要看装饰者模式是怎么实现透明添加的。
在装饰者模式的实现中,为了能够实现和原来使用被装饰对象的代码无缝衔接,是通过定义一个抽象类,让这个类实现与被装饰对象相同的接口,然后在具体实现类中,转调被装饰的对象,在转调前后添加新功能,这就实现了给被装饰对象增加功能。上面这段话最关键的就是-转调。而能转调的实现是定义统一接口。
文字描述无力,用UML图说明就清晰了
说明:
ConreteDecoratorA/ConreteDecoratorB:实际的装饰器对象,实现具体添加功能。
熟悉UML图的基本就明白了,但是像我这样不熟悉的还是写一点我们熟悉的代码描述吧。
首先是组件对象接口的定义;
/** - Created by LiCola on 2016/04/22 15:58 - 统一的组价对象接口 */
public abstract class Component {
//抽象方法 没有具体操作
public abstract void operation();
}
实现接口的对象
/** * Created by LiCola on 2016/04/22 16:00 * 具体实现组件对象接口的对象 被装饰的原始对象 */
public class ConcreteComponent extends Component {
@Override
public void operation() {
//重写方法 做基本的操作
System.out.print(" ConcreteComponent operation\n");
}
}
装饰器的抽象父类
/** * Created by LiCola on 2016/04/22 16:04 * 所有装饰器的抽象父类 持有接口 转发请求 */
public abstract class Decorator extends Component {
/** * 内部持有的组件接口对象 */
protected Component component;
/** * 依赖注入 * @param component 依赖的对象 */
public Decorator(Component component) {
this.component = component;
}
/** * 转发请求给组件对象 这里可以做一些附加操作 */
@Override
public void operation() {
component.operation();
}
}
某个具体实现例子
/** * Created by LiCola on 2016/04/22 16:08 * 某个具体装饰器实现对象,调用接口方法和具有自己的方法 并能够附加上去 */
public class ConcreteDecoratorA extends Decorator{
/** * 依赖注入 * * @param component 依赖的对象 */
public ConcreteDecoratorA(Component component) {
super(component);
}
public int operationA(){
System.out.print(" ConcreteDecoratorA operationA\n");
return 0;
}
@Override
public void operation() {
operationA();
super.operation();
}
}
以上代码只说明层级关系和逻辑,没有真实的操作。
客户端调用代码示例:
/** * Created by LiCola on 2016/04/22 16:21 */
public class Client {
public static void main(String[] args){
Component component=new ConcreteComponent();
Decorator decorator=new ConcreteDecoratorA(component);
decorator.operation();
}
}
输出结果:
ConcreteDecoratorA operationA
ConcreteComponent operation
输出结果只是打印两行字符 但是意义重大。
要知道我们只是调用了Decorator
的方法但是得到原来的基本操作ConcreteComponent
的方法和新增的ConcreteDecoratorA
的方法。就是给已经存在对象新增了功能而且没有使用继承,要知道Java的可是有单继承限制。ConcreteDecoratorA
只是一个具体实现,实际上我们可以定义很多的同样继承自Decorator类
的子类添加功能方法,然后串联起来。
装饰者模式实现的是从一个对象外部给对象添加功能,相当于改变了对象的外观,装饰过的对象,从外部系统来看已经不再是原来的对象,而是经过一系列装饰器装饰过的对象。
装饰者模式最大的好处就是灵活,它能够灵活的改变一个的对象的功能,并且是动态组合形式。另外好处就是代码复用,因为每个装饰器是独立的,可以给一个对象多次增加同一个装饰器,也可以同一个装饰器装饰不同对象。
在面向对象设计中:有一条基本规则
尽量使用对象组合,而不是对象继承来扩展和复用功能。
另外说明:
各个装饰器之间最好是完全独立的功能,不要依赖,这样在进行装饰组合的时候,才没有先后调用限制。否则会降低装饰器组合的灵活性。
装饰器模式的退化形式:
当仅仅只是添加一个功能,就没有必要再设计装饰器的抽象父类,直接在装饰器中实现组件接口,然后实现相应的拓展功能。
在Android源码中,存在很多设计模式的实现。当理解了23个设计模式看源码也会透彻得多。因为实际代码有很多限制不会存在基本的设计模式,很多时候都是变形后的设计模式实现。
比如ListView的添加头部和尾部视图/addHeadView和addFootView功能就是装饰者模式的运用。了解的MVC的都知道其实我们添加视图影响的是adapter的方法调用。实际代码的继承关键非常复杂,我就简略的画出关键UML图
Adapter:接口对象,方法很多只画一个关键的getView方法
MyBaseAdapter:是我们自动定义类继承BaseAdapter(也就是Adapter省略无关继承)用来绑定ListView显示itemView的类,是具体的组件对象,被装饰的原始对象。
HeaderViewListAdapter:退化形式的装饰器,Decorator抽象父类和子类的集合,统一成实际的装饰器对象,持有Adapter对象。
源码解析在我的另外一篇博客就不详细说明了。
Context类中也存在装饰者模式的运用,大家看源码中
abstract class Context
是抽象类就是装饰者模式的Component:组件对象接口。 public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
//省略代码 只看这个方法
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
最后谁是具体的装饰器对象呢?其实就是我们的Activity、Service和Application这些能够启动Activity的实现类啊。只是我们一般没有重写startActivity()方法由系统调用了我们没有注意到。
设计模式在写代码中经常使用到,并且非常重要。有时只是我们使用了而没有注意到它的存在。
最后附上豆瓣地址方便大家找书。UML图的画图工具是Chrome的插件UML Diagram Editor。