设计模式读书笔记(八)

"Head First 设计模式第九章"

Posted by jhljx on January 29, 2018

目录

1. 迭代器模式
2. 组合模式

迭代器模式

有很多方法可以把对象堆起来成为一个集合(collection)。

迭代器模式引入的原因:

本章开始的例子中出现了矛盾,ArrayList和数组处理方式不同,包括访问方式(一个是get函数,一个是下标索引直接访问),循环长度(一个是size()函数,一个是length()函数)。

所以我们需要封装变化的部分。在这里发生变化的就是:由不同的集合(collection)类型所造成的遍历。所以我们构建了迭代器Iterator对象,利用它来封装“遍历集合中每个对象的过程”。

Iterator iterator = breakfastMenu.createIterator();
while(iterator.hasNext()){
    MenuItem menuItem = (MenuItem)iterator.next();
}

Iterator iterator = lunchMenu.createIterator();
while(iterator.hasNext()){
    MenuItem menuItem = (MenuItem)iterator.next();
}

当我们说“集合”(collection)时,我们指的是一群对象。其存储方式可以是各式各样的数据结构,例如:列表、数组、散列表,无论用什么方式存储一律可以视为是集合,有时候也被称为聚合(aggregate)。

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    int numOfItems = 0;
    MenuItems[] menuItems;
    
    //构造器在这里
    
    //addItem在这里
    
    //下面这个函数要删除,事实上,我们根本不想要这个方法,因为它会暴露我们内部的实现
    public MenuItem[] getMenuItems() {
        return MenuItems;
    }
    
    //返回迭代器的接口,客户不需要知道餐厅菜单是如何维护菜单项的,也不需要知道迭代器是如何实现的,客户只需要直接使用这个迭代器遍历菜单项即可
    public Iterator createIterator() {
        return new DinerMenuIterator(menuItems);
    }
    
    //菜单的其他方法在这里
}

引入迭代器,符合“针对接口编程,而不是针对实现编程”的思想,我们减少了女招待和具体类之间的依赖。

迭代器模式 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式让我们能够游走于聚合内的每一个元素,而又不暴露其内部的表示。

把游走的任务放在迭代器上,而不是聚合(对象)上。这样简化了聚合的接口和实现,也让责任各得其所。聚合对象可以更专注在它应该专注的事情上面(管理对象集合)。

设计原则:单一责任

一个类应该只有一个引起变化的原因。

这个原则告诉我们,尽量让每个类保持单一责任。类的每个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。

内聚(cohesion) 这个术语是用来度量一个类或模块紧密地达到单一目的或责任。

当一个模块或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚 。反之,当被设计成支持一组不相关的功能时,我们说它具有低内聚。

内聚是一个比单一责任原则更普遍的概念,但两者的关系其实很密切。

第九章 组合模式

组合模式:

管理菜单的问题已经到了一个迭代器无法解决的新维度。

组合模式 允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

这个模式能够创建一个树形结构,在同一个结构中处理嵌套菜单和菜单项组。通过将菜单和项放在相同的结构中,我们创建了一个“整体/部分”层次结构,即由菜单和菜单项组成的对象树。一旦有了丰富的大菜单,我们就可以使用这个模式来“统一处理个别对象和组合对象。”

组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。

使用组合结构,我们能把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别

组合模式的类图

组合包括组件。 组件有两种:组合和叶节点元素。(听起来有点像递归)

菜单组件的角色是为叶节点和组合节点提供一个共同的接口。

组合模式的类好像违反了“一个类,一个责任”的原则,不但要管理层次结构,还要执行菜单的操作。

组合模式以单一责任设计原则换取透明性(transparency)。透明性即通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁(一个元素究竟是组合还是叶节点,对客户是透明的)。

但如果将类设计为一个责任,保证了设计上的安全性,却失去了透明性,客户代码必须通过条件语句和instanceof操作符处理不同类型的节点。

因此,组合模式是一种折衷的设计模式。

如何实现菜单项的createIterator()方法: (注意这是空对象“设计模式”的一个例子)

  • 选择1:返回null
  • 选择2:返回一个迭代器,而这个迭代器的hashNext()永远返回false(第二种选择更好)

在组合迭代器的printVegetarianMenu()方法中加入try…catch语句不是一种很好的编程形式,但是我们无需判断菜单组件的运行时类型,保证了透明性,可以使我们能够统一地处理菜单和菜单项。当然也可以在Menu的isVegetarian()方法中返回false,但是这样不能体现这是Menu没有支持的操作。

组合模式的应用:图像界面中顶层组件(像是Frame或Panel)包含着其他组件(像菜单、文字面板、滚动条、按钮)所以GUI包含了若干部分,但是当你显示它的时候,你认为它是一个整体。这种包含其他组件的组件称为组合对象,而没有包含其他组件的组件称为叶节点对象

  • 策略模式:封装可互换的行为,并使用委托决定使用哪一个
  • 适配器模式:改变一个或多个类的接口
  • 迭代器模式:提供一个方式来遍历集合,而无须暴露集合的实现
  • 外观模式:简化一群类的接口
  • 组合模式:客户可以将对象的集合以及个别的对象一视同仁
  • 观察者模式:当某个状态改变时,允许一群对象被通知到