=单例模式=
单例模式需要考虑的重要问题是其生存周期问题,一种是不死鸟,永远不销毁,最为简单,但是占用了资源
另一种是有生存周期, 但是又要考虑其引用可能无效的问题
* Lifetime: Dead reference
* Double check locking
=工厂模式=
工厂模式是很常用的模式, 常见的有
*简单工厂
*抽象工厂
*工厂方法
=生成器模式=
=原型模式=
这里只是简单地用相应类图来表示, 个中滋味, 在应用中自己慢慢体会吧
相似的一点是抽象的东西有具体的实现, 至于到底用哪个具体的实现, 交给工厂来创建吧
至于这个工厂, 视问题域的复杂性,可以是抽象的, 也可以是具体的,工厂模式大体如此
General Responsibility Assignment
Software Patterns 通用职责分配软件模式
模式名称
|
描述(问题/解决方案)
|
信息专家模式Information Expert
|
问题:对象设计和职责分配的一般原则是什么?
解决方案:将职责分配给拥有履行一个职责所必需信息的类--即信息专家。(也就是将职责分配给一个类,这个类必须拥有履行这个职责所需要的信息。)
|
创建者模式Creator
|
问题:谁应该负责产生类的实例(对应于GoF设计模式系列里的“工厂模式”)
解决方案:如果符合下面的一个或多个条件,则将创建类A实例的职责分配给类B.
.类B聚合类A的对象。
.类B包含类A的对象。
.类B记录类A对象的实例。
.类B密切使用类A的对象。
.类B初始化数据并在创建类A的实例时传递给类A(类B是创建类A实例的一个专家)。
在以上情况下,类B是类A对象的创建者。
|
控制器模式
Controller
|
问题:谁处理一个系统事件?
解决方案:当类代表下列一种情况时,为它分配处理系统事件消息的职责。
.代表整个系统、设备或子系统(外观控制器)。
.代表系统事件发生的用例场景(用例或回话控制器)。
|
低耦合Low Coupling
|
问题:如何支持低依赖性以及增加重用性?
解决方案:分配职责时使(不必要的)耦合保持为最低。
|
高内聚High Cohesion
|
问题:如何让复杂性可管理?
解决方案:分配职责时使内聚保持为最高。
|
多态模式Polymorphism
|
问题:当行为随类型变化而变化时谁来负责处理这些变化?
解决方案:当类型变化导致另一个行为或导致行为变化时,应用多态操作将行为的职责分配到引起行为变化的类型。
|
纯虚构模式Pure Fabrication
|
问题:当不想破坏高内聚和低耦合的设计原则时,谁来负责处理这些变化?
解决方案:将一组高内聚的职责分配给一个虚构的或处理方便的“行为”类,它并不是问题域中的概念,而是虚构的事务,以达到支持高内聚、低耦合和重用的目的。
|
中介模式Indirection
|
问题:如何分配职责以避免直接耦合?
解决方案:分配职责给中间对象以协调组件或服务之间的操作,使得它们不直接耦合。
|
受保护变化模式Protected Variations
|
问题:如何分配职责给对象、子系统和系统,使得这些元素中的变化或不稳定的点不会对其他元素产生不利影响?
解决方案:找出预计有变化或不稳定的元素,为其创建稳定的“接口”而分配职责。
|
这些更象是一些OOD的原则, 模式会有很多, 但是万变不离其宗, 大都遵循着一些基本的原则
-
OCP(Open-Closed Principle)
- DIP(Dependency Inversion
Principle)
-
LSP(Liskov Substitution
Principle)
- ISP(Interface Insolation
Principle)
- SRP(Single Resposibility
Principle)
- CARP(Composite/Aggregate Reuse
Principle)
- LoD(Law Of Demeter):don't talk
to stranger
之后我们来详细讨论这些原则
大名鼎鼎的GOF的设计模式是最著名的一本里程碑的作品
=模式分类=
=模式之间的关系=
=如何应用模式=
DP中的引言说得很好,如何应该模式来解决设计问题
* 寻找合适的对象
对象包括数据和操作, 对象在收到请求(或消息)后, 执行相应的操作
客户请求是使对象执行操作的唯一方法, 操作又是对象改变内部数据的唯一方法
(这就是封装的意义,之所以强调对象的成员应该是私有的原因)
OOD最困难的部分就是将系统分解成对象集合,因为要考虑许多因素:
封装,粒度,信赖关系,灵活性,性能,演化,复用等等,它们之间也互相有所影响或冲突.
设计模式可以帮助我们确定那些并不明显的抽象和描述这些抽象的对象,如Strategy, State,etc.
==决定对象的粒度==
如何决定对象的大小,数目以及范围呢, 设计模式亦有所帮助:
Facade 描述了怎样用对象表示完整的子系统
Flyweight
Abstact Factory
Builder
Visitor
Command
==指定对象接口==
对象接口描述了该对象所能接受的全部请求的集合, 也就是它能够提供哪些服务(方法)
当给对象发送请求时, 所引起的具体操作既与请求本身有关,又与接受对象有关
支持相同请求的不同对象可能对请求激发的操作有不同的实现(动态绑定和多态)
而设计模式通过确定接口的主要组成部分及经接口发送的数据类型, 来帮助你定义接口.
DP也许还会告诉你接口中不应包括哪些东西, 比如Memento模式所规定的接口
DP也指定了接口之间的关系,特别地,它常要求一些类具有相同或相似的接口,或对一些类的接口作出一些限制
如Decorator, Proxy模式要求修饰/代理对象和被修饰/受代理的对象接口保持一致
Visitor模式中Vistor接口必须反映能访问的对象的所有类
==描述对象的实现==
* 类继承还是接口继承呢
* 针对接口编程,而不是针对实现编程
==运用复用机制==
1.优先使用对象组合,而不是类继承
2.委托
3.继承和泛型的比较
==关联运行时刻和编译时刻的结构==
==设计应支持变化==
* 设计中常出现的问题
** 通过显式地指定一个类来创建对象
*** Factory , Prototype
** 对特殊操作的依赖
*** Chain of Reponsibility, Command
** 对硬件和软件平台的依赖
*** Abstract Factory, Bridge
** 对对象表示或实现的依赖
** 算法依赖
** 紧耦合
*** Abstract Factory, command, facade, mediator, observere,chain of responsibility
** 通过生成子类来扩充功能
*** Bridge, Chain of Reponsibility, composite, Decorator, Observer, Strategy
** 不能方便地对类进行修改
*** Adapter, Decorator, visitor
=如何选择设计模式=
* 考虑设计模式是如何解决设计问题的
* 浏览模式的意图部分
* 研究模式怎样互相关联
* 研究目的相似的模式
* 检查重新设计的原因
* 考虑你的设计中哪些是可变的
=怎样使用设计模式=
* 大致浏览一遍模式
* 回头研究结构部分
* 看代码示例部分
* 选择模式参考者的名字, 使它们在应用上下文中有意义
* 定义类
* 定义模式中专用于应用的操作名称
* 实现执行模式中责任和协作的操作
啥叫模式? Patterns in solutions come from patterns in problems.
针对某一类经常出现的问题所采取的行之有效的解决方案
"A pattern is a solution to a problem in a context."
"Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice."(Christopher Alexander -- A Pattern Language)
模式的四个基本要素:
1. 模式名称pattern name
2. 问题problem
3. 解决方案solution
4. 效果consequences
如何描述设计模式(十大特点)
1. 意图:描述,别名
2. 动机:场景
3. 适用性: 什么情况下
4. 结构: 类图, 序列图
5. 参考者
6. 协作
7. 效果
8. 实现
9. 应用
10. 相关模式
在实践中学习是最佳的方式, 所以先要掌握每个模式的十大特点,更加重要的是在实际应用中学习, 在水中学会游泳
以迭代器模式为例, Java中有一个Iterator接口
1 public interface Iterator
2 {
3 /**
4 * Tests whether there are elements remaining in the collection. In other
5 * words, calling <code>next()</code> will not throw an exception.
6 *
7 * @return true if there is at least one more element in the collection
8 */
9 boolean hasNext();
10
11 /**
12 * Obtain the next element in the collection.
13 *
14 * @return the next element in the collection
15 * @throws NoSuchElementException if there are no more elements
16 */
17 Object next();
18
19 /**
20 * Remove from the underlying collection the last element returned by next
21 * (optional operation). This method can be called only once after each
22 * call to <code>next()</code>. It does not affect what will be returned
23 * by subsequent calls to next.
24 *
25 * @throws IllegalStateException if next has not yet been called or remove
26 * has already been called since the last call to next.
27 * @throws UnsupportedOperationException if this Iterator does not support
28 * the remove operation.
29 */
30 void remove();
31 }
32
假如你的类中有一些聚集关系, 那么考虑增加一个iterator方法,以实现下面这个接口
public interface Iterable
{
/**
* Returns an iterator for the collection.
*
* @return an iterator.
*/
Iterator iterator ();
}
返回你自己实现的ConcreteIterator类, 这个ConcreteIterator当然是实现了
Iterator接口的
你会发现在遍历和迭代类中的这个成员的聚集元素时会有不同的感觉, 因为这个Iterator与实现是分离的.
你的类终归是给自己或别人使用的,在调用者的眼里, 非常简单, 管你里面是怎么实现的呢,
反正我知道你能给我一个迭代器就够了, 这里面就体现了面向接口编程的好处. 也就是按契约编程
自Java5以来提供的
BlockingQueue是一种特殊的队列, 它 是支持两个附加操作的
Queue
,这两个操作是:检索元素时等待队列变为非空,以及存储元素时等待空间变得可用。
以JDK中的例子略加改写如下
1 import java.util.concurrent.ArrayBlockingQueue;
2 import java.util.concurrent.BlockingQueue;
3
4 class Producer implements Runnable {
5 private final BlockingQueue queue;
6
7 Producer(BlockingQueue q) {
8 queue = q;
9 }
10
11 public void run() {
12 try {
13 while (true) {
14 queue.put(produce());
15 }
16 } catch (InterruptedException ex) {
17 System.out.println("produce interrupted " + ex.getMessage());
18 Thread.currentThread().interrupt();
19 //return;
20 }
21 }
22
23 Object produce() {
24 System.out.println("produce laugh");
25 return "haha";
26 }
27 }
28
29 class Consumer implements Runnable {
30 private final BlockingQueue queue;
31
32 Consumer(BlockingQueue q) {
33 queue = q;
34 }
35
36 public void run() {
37 try {
38 while (true) {
39 consume(queue.take());
40 }
41 } catch (InterruptedException ex) {
42 System.out.println("consume interrupted " + ex.getMessage());
43 Thread.currentThread().interrupt();
44 }
45 }
46
47 void consume(Object x) {
48 System.out.println("consume laugh "+ x);
49 }
50 }
51
52 public class BlockingQueueTest {
53 public static void main(String args[]) {
54 BlockingQueue q = new ArrayBlockingQueue(10);
55 Producer p = new Producer(q);
56 Consumer c1 = new Consumer(q);
57 Consumer c2 = new Consumer(q);
58 Thread pTh = new Thread(p);
59 pTh.start();
60 Thread cTh1 = new Thread(c1);
61 cTh1.start();
62 Thread cTh2 = new Thread(c2);
63 cTh2.start();
64 try {
65 Thread.sleep(3000);
66 }catch (Exception e) {
67 // TODO: handle exception
68 }
69 pTh.interrupt();
70 cTh1.interrupt();
71 cTh2.interrupt();
72 }
73 }
74