提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴漏该对象的内部表示
【产品】:嘿,有一个好消息,咱们旗下的餐厅把月巴克的咖啡店吞并了!太棒了!年终奖稳了!
【开发】:Yeah!Yeah!Yeah!
【产品】:但是他们好像反应一个问题,月巴克的点餐系统好像不兼容我们的体系,怎么回事?不就是一个菜单吗?
【开发】:Oh!No!一定它们的 数据结构 不一样导致的,遍历出现了问题!
【产品】:那怎么办?BOSS,你们一起想想办法吧!
【开发】:老大,我们能不能把遍历方法抽取出来啊?我们遍历操作就可以不用考虑各种细节了,只需要管遍历类就好了。
【BOSS】:什么遍历类的,这叫 迭代器 好吗!其实JDK对于迭代器已经维护的很好了,但是咱们这业务也有一点特殊性,就按你说的办吧,办不好的话,刚才说的年终奖就没了。
【开发】:哦,好的(脸上笑嘻嘻,心里MMP)
定义迭代器持有者 非必须代码
/**
* ******************************
* description: 迭代器持有者
* ******************************
*/
public interface MyContainer {
MyIterator getIterator();
}
迭代器接口
/**
* ******************************
* description: 迭代器接口
* ******************************
*/
public interface MyIterator {
boolean hasNext();
Object next();
}
迭代器工作类 食物菜单
public class FoodRepository implements MyContainer {
String[] names = {"宫保鸡丁", "麻辣香锅", "油闷大虾"};
@Override
public MyIterator getIterator() {
return new NameIterator();
}
private class NameIterator implements MyIterator {
private int index;
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public Object next() {
return hasNext() ? names[index++] : null;
}
NameIterator() {
index = 0;
}
}
}
迭代器工作类 咖啡菜单
public class CoffeeRepository implements MyContainer {
List<String> names = Arrays.asList("雀巢咖啡", "黑糖玛奇朵", "半点寂寞");
@Override
public MyIterator getIterator() {
return new NameIterator();
}
private class NameIterator implements MyIterator {
private int index;
@Override
public boolean hasNext() {
return index < names.size();
}
@Override
public Object next() {
return hasNext() ? names.get(index++) : null;
}
NameIterator() {
index = 0;
}
}
}
测试类 观察调用的表现形式
public class App {
public static void main(String[] args){
// 餐厅菜单
FoodRepository food = new FoodRepository();
MyIterator foodIterator = food.getIterator();
while (foodIterator.hasNext()) {
System.out.println("Food: -> " + foodIterator.next());
}
CodeUtils.spilt();
// 咖啡菜单
CoffeeRepository coffee = new CoffeeRepository();
MyIterator coffeeIterator = coffee.getIterator();
while (coffeeIterator.hasNext()) {
System.out.println("Coffee: -> " + coffeeIterator.next());
}
}
}
public class App {
public static void main(String[] args){
// JDK
List<String> names = Arrays.asList("Han", "John", "Tomams");
Iterator<String> iterable = names.iterator();
while (iterable.hasNext()) {
System.out.println("JDK Iterator: -> " + iterable.next());
}
CodeUtils.spilt();
// JDK
names.forEach(s -> System.out.println("JDK forEach: -> " + s));
}
}
因此对于业务上没有什么要求且常见的数据结构,我们不再需要自行定义迭代器
迭代器模式的设计思路:
简单来说,
如果看着有点模棱两可,就看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面
说明:迭代器类在设计中仅仅包含集合迭代的作用,它是把原本数据结构中的遍历抽取出来,达到 高内聚 的效果。
所谓高内聚:当一个模块或一个类被设计成只支持一组相关功能时,我们说它具有 高内聚 的特征。
举一个不是很恰当的例子,我们都用自动贩卖机买过水,付钱之后它会自动滚出来,大家有没有想过它是怎么实现这个效果的呢?它支持瓶装的,罐装的,甚至还支持袋装的,方便面,口红等等五花八门的产品,它的内部结构可能都各不相同,但是最终的表现效果就是我们直接从出口处拿即可,这是不是迭代器模式的一种体现呢?
将对象组合成树形结构以表示 “部分-整体” 的层次结构,Composite使得用户对单个对象和组合对象的使用具有一致性
说人话:想想Java里的File类
组合模式 不是 一堆模式的组合!
【开发】:老大,我在写菜单类的时候感觉好痛苦啊!
【BOSS】:怎么了?
【开发】:菜单有真正的菜品,还有父级菜单啊,它们俩得维护两套逻辑,混在一起好难受!
【BOSS】:你在操作文件的时候怎么不觉得难受?你咋不动动脑子想着抽象一下啊!
【开发】:对啊!我去改代码!
定义抽象行为
/**
* ******************************
* description: 定义抽象行为
* ******************************
*/
public abstract class MenuComponent {
public String name;
/***
* 添加
*/
public abstract void add(MenuComponent component) throws Exception;
/***
* 移除
*/
public abstract void remove(MenuComponent component) throws Exception;
/***
* 获取菜单名
*/
public abstract String getName();
/***
* 获取子菜单
*/
public abstract MenuComponent getChild(int i) throws Exception;
/***
* 打印菜单
*/
public abstract void print();
}
实现 “整体”
public class Menu extends MenuComponent{
List<MenuComponent> menuComponents = new ArrayList<>();
public Menu(String name) {
this.name = name;
}
@Override
public void add(MenuComponent component) {
this.menuComponents.add(component);
}
@Override
public void remove(MenuComponent component) {
this.menuComponents.remove(component);
}
@Override
public String getName() {
return this.name;
}
@Override
public MenuComponent getChild(int i) {
return menuComponents.get(i);
}
@Override
public void print() {
System.out.println("当前菜单项: " + getName());
for (MenuComponent component : menuComponents) {
component.print();
}
}
}
实现 "部分"
public class MentItem extends MenuComponent{
public MentItem(String name) {
this.name = name;
}
@Override
public void add(MenuComponent component) throws Exception {
throw new Exception("无法添加");
}
@Override
public void remove(MenuComponent component) throws Exception {
throw new Exception("无法移除");
}
@Override
public String getName() {
return this.name;
}
@Override
public MenuComponent getChild(int i) throws Exception {
throw new Exception("无子节点");
}
@Override
public void print() {
System.out.println(" 食物名: " + getName());
}
}
测试类
public class App {
/***
* 推荐代码阅读顺序:
*
* @see MenuComponent
* @see Menu
* @see MentItem
*/
public static void main(String[] args) {
Menu meat = new Menu("炒菜类");
MentItem item1 = new MentItem("宫保鸡丁");
MentItem item2 = new MentItem("剁椒鸡蛋");
MentItem item3 = new MentItem("鱼香肉丝");
Menu vegetable = new Menu("素食");
MentItem v1 = new MentItem("酸辣土豆丝");
MentItem v2 = new MentItem("爆炒包菜");
meat.add(item1);
meat.add(item2);
meat.add(item3);
vegetable.add(v1);
vegetable.add(v2);
meat.add(vegetable);
meat.print();
}
}
/***
* 输出内容:
*
* 当前菜单项: 炒菜类
* 食物名: 宫保鸡丁
* 食物名: 剁椒鸡蛋
* 食物名: 鱼香肉丝
* 当前菜单项: 素食
* 食物名: 酸辣土豆丝
* 食物名: 爆炒包菜
*/
组合模式的设计思路:
简单来说,
如果看着有点模棱两可,就看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面
依然是一个不太恰当的例子,我们在操作文件和文件夹的时候,都有其移动,复制,重命名,查看文件大小等等功能,对于Java来说,它的底层实现是有一个 是否是文件夹
的方法来区分,但实际上这也是组合模式的根本思想,即对于表示 部分 的对象,和 整体 的对象,拥有统一的操作行为
GitHub地址