headfirst设计模式pdf
HeadFirst设计模式(中文版)作者EricFreeman;ElElisabethFreeman是作家、讲师和技术顾问。共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式,小编为大家准备了本书的PDF版的,内容非常的高清,快来下载吧
书籍介绍《Head First设计模式(中文版)》编辑推荐:强大的写作阵容。《Head First设计模式(中文版)》作者Eric Freeman;ElElisabeth Freeman是作家、讲师和技术顾问。Eric拥有耶鲁大学的计算机科学博士学位,
E1isabath拥有耶鲁大学的计算机科学硕士学位。Kathy Sierra FHBert Bates是畅销的HeadFirst系列书籍的创立者,也是Sun公司Java开发员认证考试的开发者。《Head First设计模式(中文版)》的产品设计应用神经生物学、
认知科学,以及学习理论,这使得此书能够将这些知识深深地印在你的脑海里,不容易被遗忘。《Head First设计模式(中文版)》的编写方式采用引导式教学,不直接告诉你该怎么做,而是利用故事当作引子,
带领读者思考并想办法解决问题。解决问题的过程中又会产生一些新的问题,再继续思考、继续解决问题,这样可以加深体会。作者以大量的生活化故事当背景,例如第1章是鸭子,
第2章是气象站,第3章是咖啡店,书中搭配大量的插图(几乎每一页都有图),所以阅读起来生动有趣,不会感觉到昏昏欲睡。作者还利用歪歪斜斜的手写字体,增加“现场感”
。精心设计许多爆笑的对白,让学习过程不会太枯燥。还有模式告白节目,将设计模式拟人化成节目来宾,畅谈其内在的一切。《Head First设计模式(中文版)》
大量采用uML的class Diagram(Static Structure Diagram)。书中的例子程序虽然都是用JaVa编写,但是《Head First设计模式(中文版)》所介绍的内容对于任何00语言的用户都适用,
包括c++和c孝。每一章都有数目不等的测验题。每章最后有一页要点整理,这也是精华所在,我都是利用这一页做复习。 Head First 设计模式(中文版)
目录: 1设计模式入门欢迎来到设计模式世界 2观察者模式让你的对象知悉现况 3装饰者模式装饰对象 4工厂模式烘烤OO的精华 5单件模式独一无二的对象
6命令模式封装调用 7适配器模式与外观模式随遇模式 8模板方法模式封装算法 9选代器与组合模式管理良好的集合 10状态模式事物的状态 11代理模式控制对象访问 12复合模式模式中的模型 13与设计模式相处真实世界中的模式
Head First设计模式的种类介绍了23种设计模式
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
相关内容图片预览Head First设计模式:迭代器模式迭代器模式
因为这一章涉及到两个模式,内容有点多,还有一个组合模式留到下一篇写吧。
有许多种方法可以把对象堆起来成为一个集合(collection)。你可以把它们放进数组、堆栈、列表或者是散列表(Hashtable)中,这是你的自由。每一种都有它自己的优点和适合的使用时机,但总有一个时候,你的客户想要遍历这些对象,而当他这么做时,你打算让客户看到你的实现吗?我们当然希望最好不要!这太不专业了。本章的迭代器模式将能让客户遍历你的对象而又无法窥视你存储对象的方式。
先来看看迭代器模式的定义:
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
题例:有两家餐厅,披萨店和煎饼店,它们合并了,虽然可以在一个地方同时想用煎饼屋的早餐和餐厅的午餐,但是煎饼屋的菜单用用的ArrayList记录菜单的,而餐厅用的是数组,而两家餐馆都不愿意修改自己的实现。毕竟有很多代码依赖它们。
幸好两家都统一实现了MenuItem:
//菜单项,保存了菜单信息public class MenuItem { private String name; private String description; private boolean vegetarian; private double price; public MenuItem(String name, String description, boolean vegetarian, double price) { super(); this.name = name; this.description = description; this.vegetarian = vegetarian; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isVegetarian() { return vegetarian; } public void setVegetarian(boolean vegetarian) { this.vegetarian = vegetarian; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; }}
再来看看两家店各自的菜单实现:
煎饼店:用ArrayList
// 煎饼餐店对象,用ArrayList保存了菜单。public class PancakeHouseMenu { ArrayList<MenuItem> menuItems; public PancakeHouseMenu() { menuItems = new ArrayList<MenuItem>(); addItem("煎饼1号", "牛肉煎饼", false, 2.99); addItem("煎饼2号", "素食煎饼", true, 1.49); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu = new MenuItem(name, description, vegetarian, price); menuItems.add(menu); } public ArrayList<MenuItem> getMenuItems() { return menuItems; }}
披萨店:用数组
// 披萨餐厅对象,用数组保存了菜单信息。public class PizzaHouseMenu { static final int MAX_ITEMS = 2; int numberOfItems = 0; MenuItem[] menuItems; public PizzaHouseMenu() { menuItems = new MenuItem[MAX_ITEMS]; } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu = new MenuItem(name, description, vegetarian, price); if (numberOfItems >= MAX_ITEMS) System.out.println("对不起,菜单数量已满"); else menuItems[numberOfItems++] = menu; } public MenuItem[] getMenuItems() { return menuItems; }}
有两种不同的菜单表现方式,这会带来什么问题?
假设你是一个女招待下面是你做的事,你会怎么办?
printMenu(); 打印出菜单上的每一项
printBreakfastMenu(); 只打印早餐
printLunchMenu(); 只打印午餐
printVegetarianMenu(); 打印所有的素食菜单
isItemVegetarian(name); 查询指定的菜品是否是素食
指定项的名称,如果该项是素食的话,返回true,否则返回false
打印没分菜单上的所有项,必须调用PancakeHouseMenu和PizzaHouseMenu的getMenuItenm()方法,来取得它们各自的菜单项,两者返回类型是不一样的。
PancakeHouseMenu pancakeHouseMenu=new PancakeHouseMenu();ArrayList breakfastItems=pancakeHouseMenu.getMenuItems(); PizzaHouseMenu pizzaHouseMenu=new PizzaHouseMenu();MenuIten[] linchItenms=pizzaHouseMenu.getMenuItens();
打印菜单需要的数组和集合,用循环将数据一一列出来
public class Client { public static void main(String[] args) { // 先获得煎饼餐厅的菜单集合 PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); ArrayList<MenuItem> menusOfPancake = pancakeHouseMenu.getMenuItems(); // 在获得披萨餐厅的菜单数组 PizzaHouseMenu pizzaHouseMenu = new PizzaHouseMenu(); MenuItem[] menusOfPizza = pizzaHouseMenu.getMenuItems(); //我们用循环将数据一一列出来 for (int i = 0; i < menusOfPancake.size(); i++) { MenuItem menu = menusOfPancake.get(i); System.out.print(menu.getName() + ",价格:"); System.out.print(menu.getPrice() + ","); System.out.print(menu.getDescription() + "\n"); } System.out.println(); for (int i = 0; i < menusOfPizza.length; i++) { MenuItem menu = menusOfPizza; System.out.print(menu.getName() + ",价格:"); System.out.print(menu.getPrice() + ","); System.out.print(menu.getDescription() + "\n"); } }}
我们总是需要处理这两个菜单的遍历,如果还有第三家餐厅以不同的方式实现菜单集合,我们就需要有第三个循环。
可以封装遍历吗?
可以封装变化的部分。很明显,这里发生的变化是:由不同的集合类型所造成的遍历。但是,这能够被封装吗?让我们来看看这个想法……
要便利煎饼餐厅,我们需要使用ArrayList的size()和get()方法;
要便利披萨餐厅,我们需要使用数组的length字段和在中括号中输入索引;
现在我们创建一个对象,将它称为迭代器(Iterator),利用它来封装“遍历集合内的每个对象的过程”;
想要在餐厅菜单中加入一个迭代器,我们需要先定义迭代器接口,然后为披萨餐厅创建一个迭代器类:
public interface Iterator { boolean hasNext(); Object next();}public class PizzaIterator implements Iterator { MenuItem[] items; int position = 0; public PizzaIterator(MenuItem[] items) { this.items = items; } // 判断数组下一个索引是否还有元素 public boolean hasNext() { if(position >= items.length || items[position] == null) return false; else return true; } // 获得当前索引位置的元素 public Object next() { MenuItem item = items[position++]; return item; }} public class PancakeIterator implements Iterator { ArrayList<MenuItem> items; int position = 0; public PancakeIterator(ArrayList<MenuItem> items) { this.items = items; } // 判断数组下一个索引是否还有元素 public boolean hasNext() { if(position >= items.size() || items.get(position) == null) return false; else return true; } // 获得当前索引位置的元素 public Object next() { MenuItem item = items.get(position); return item; }}
创建好迭代器后,改写披萨餐厅的代码,创建一个PizzaMenuIterator,并返回给客户:
public class PizzaHouseMenu { static final int MAX_ITEMS = 2; int numberOfItems = 0; MenuItem[] menuItems; public PizzaHouseMenu() { menuItems = new MenuItem[MAX_ITEMS]; addItem("披萨1号", "素食披萨", true, 4.99); addItem("披萨2号", "海鲜蛤蜊披萨", true, 5.99); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu = new MenuItem(name, description, vegetarian, price); if (numberOfItems >= MAX_ITEMS) System.out.println("对不起,菜单数量已满"); else menuItems[numberOfItems++] = menu; } public Iterator createIterator() { return new PizzaIterator(menuItems); }} public class PancakeHouseMenu { ArrayList<MenuItem> menuItems; public PancakeHouseMenu() { menuItems = new ArrayList<MenuItem>(); addItem("煎饼1号", "牛肉煎饼", false, 2.99); addItem("煎饼2号", "素食煎饼", true, 1.49); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menu = new MenuItem(name, description, vegetarian, price); menuItems.add(menu); } public Iterator createIterator() { return new PancakeIterator(menuItems); }}
我们不再需要getMenuItems()方法,而是用createIterator()方法代替,用来从菜单项数组创建一个迭代器,并把他返回给客户,返回迭代器接口。客户不需要知道餐厅菜单使如何实现维护的,也不需要知道迭代器是如何实现的。客户只需直接使用这个迭代器遍历菜单即可。下面修改一下客户类的调用:
public class Waitress { PancakeHouseMenu pancake; PizzaHouseMenu pizza; public Waitress(PancakeHouseMenu pancake, PizzaHouseMenu pizza) { this.pancake = pancake; this.pizza = pizza; } public void printMenu() { Iterator pizzaIterator = pizza.createIterator(); printMenu(pizzaIterator); Iterator pancakeIterator = pancake.createIterator(); printMenu(pancakeIterator); } private void printMenu(Iterator iterator) { while(iterator.hasNext()) { MenuItem menu = (MenuItem)iterator.next(); System.out.print(menu.getName() + ",价格:"); System.out.print(menu.getPrice() + ","); System.out.print(menu.getDescription() + "\n"); } }}public class Client { public static void main(String[] args) { PancakeHouseMenu pancake = new PancakeHouseMenu(); PizzaHouseMenu pizza = new PizzaHouseMenu(); Waitress waitress = new Waitress(pancake, pizza); waitress.printMenu(); }}
输出结果:
到目前为止,我们将客户调用与餐厅的菜单数据接口解耦了,客户调用再也不用为每一个不同数据结构的菜单编写一套遍历的代码了。
到目前为止,我们做了些什么?
我们现在使用一个共同的迭代器接口(Iteraotr)实现了两个具体类(PizzaIterator和PancakeIterator)。这两个具体类都实现了各自的hasNext()方法和next()方法。
然后再PancakeHouseMenu和PizzaHouseMenu两个类中,创建一个createIterator()方法返回各自的迭代器,在Waitress类中,使用这两个餐厅对象返回的迭代器打印菜单。这时Waitress类和Client类再也不需要关心存放菜单的数据结构,之关心能从迭代器中获得菜单就好。
迭代器模式给你提供了一种方法,可以顺序访问一个聚集对象的元素,而又不用知道内部是如何表示的。你已经在前面的两个菜单实现中看到了这一点。在设计中使用迭代器的影响是明显的:如果你有一个统一的方法访问聚合中的每一个对象,你就可以编写多态的代码和这些聚合搭配,使用如同前面的printMenu()方法一样,只要有了迭代器这个方法根本不用管菜单究竟是由数组还是集合或者其他的数据结构来保存的。
另外一个对你的设计造成重要影响的,是迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得更简洁,也可以让聚合更专注它所应该专注的事情上面,而不必去理会遍历的事情。
让我们检查类图,将来龙去脉拼凑出来……
先看看Aggregate接口,有一个共同的接口提供所有的聚合使用,这对客户代码是很方便的,将客户代码从集合对象的实现解耦。
接下来看看ConcreteAggregate类,这个具体聚合持有一个对象的集合,并实现一个方法,利用此方法返回集合的迭代器。每一个具体聚合都要负责实例化一个具体的迭代器,次迭代器能够便利对象集合。
接下来是Iterator接口,这是所有迭代器都必须实现的接口,它包含一些方法,利用这些方法可以在集合元素之间游走。你可以自己设计或者使用java.util.Iterator接口。
最后是具体的迭代器,负责遍历集合。
单一责任
如果我们允许我们的聚合实现他们内部的集合,以及相关的操作和遍历的方法,又会如何?我们已经知道这回增加聚合中的方法个数,但又怎么样呢?为什么这么做不好?
想知道为什么,首选需要认清楚,当我们允许一个类不但要完成自己的事情,还同时要负担更多的责任时,我们就给这个类两个变化的原因。如果这个集合变化的话,这个类也必须要改变,如果我们遍历的方式改变的话,这个类也必须跟着改变。所以,引出了设计原则的中心:
单一责任:一个类只有一个引起变化的原因。
类的每个责任都有改变的潜在区域。超过一个责任,意味着超过一个改变区域。这个原则告诉我们,尽量让每一个类保持单一责任。
内聚(cohesion)这个术语你应该听过,它用来度量一个类或者模块紧密地达到单一目的或责任。
当一个模块或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚;反之,当被设计成支持一组不相关的功能时,我们说它具有低内聚。
内聚是一个比单一职责更普遍的概念,但两者其实关系是很密切的。遵守这个原则的类更容易有很高的凝聚力,而且比背负许多职责的低内聚类更容易维护。
以上就是迭代器模式的一些内容。