<4>合理运用设计模式
现在大多的设计模式设计的书本对”设计模式”讲解得都比较书面化一些,对于有些读者理解起来比较困难,所以我想,结合我的理解,在此再次旧话重谈,再次讲一下设计模式.因为它真的非常重要,不管你使用何种语言来写程序.特别是在实际的项目里面.它是重之又重.
在了解设计模式之前,大家最好还是通过网站和一些书本先了解一下这六条设计原则
开-闭原则
里氏代换原则
依赖倒转原则
接口隔离原则
合成/聚合原则
迪米特原则
<一>享元模式(Flyweight Pattern)
先让我们来看一下享元模式的定义:
享元模式属一种结构模式,它可以共享的方式高效的支持大量细粒的共享.它具有一个内蕴状态和一个外蕴状态.前者是存在对象内部,不会随环境改变而改变,后者是随环境改变而改变的.它不可以是共享状态.换句话说,它们是相互独立的,互不相干的.
其实说”内蕴”和”外蕴”可能太过于书面化,你可以把它理解成内部引用对象和外部传入的对象,外部不可以改变内部对象,内部对象也改变不了外部对象.其实享元这个词你完全可以的理解成共享.其实在java世界中String对象就是一个典型的享元模式,想起来了吗?还记得一个经典的例子:java中的equal()不等于”==”号吗? 好,下面来让我们来看一个例子来说明这一点.理解其实还没有它的定义的那么复杂.
//******************************************************************************************
package test;
import java.util.HashMap;
public class test {
public static void main(String[] args) {
FlyWeightFactory f = new FlyWeightFactory();//构建享元工厂.使用享元必须使用工厂
FlyWeight w = f.CreateInstance('a');//构建一个实列,让虚类接收.’a’为外蕴对象.
w.operation("abcdefg");//”abcdefg”在这里就是外蕴对象.
w.operation("ABCDEFG");这里也是外蕴对象.
//以上两个String即”abcdefg”和”ABCDEFG”共享一个’a’
FlyWeight wf = f.CreateInstance('b');//内蕴对象.
wf.operation("abcdefg");//外蕴对象
wf.operation("ABCDEFG");//这里也是外蕴对象.
//以上两个String即”abcdefg”和”ABCDEFG”共享一个’b’
}
}
//享元对象虚类
abstract class FlyWeight {
abstract void operation(String state);
}
//具体享元类.
class CharacterFlyWeight extends FlyWeight {
private Character c = null;
public CharacterFlyWeight(Character c) {
this.c = c;
}
@Override
void operation(String state) {
// TODO Auto-generated method stub
System.out.println(" Character State = " + c + " String state = " + state);
}
}
//享元工厂类.
class FlyWeightFactory {
//请留意一下这个HashMap.
private HashMap map = new HashMap();
private FlyWeight flyweight;
public FlyWeightFactory() {
}
public FlyWeight CreateInstance(Character c) {
if (map.containsKey(c)) {//如果这个map中包括c对象
return (FlyWeight) map.get(c);//还是返回以前key即c的对应的那个对象.注意内蕴对象主要是通过这个来实现的.
}
else {
//重新构建一个对象.并使map再次包含它.
FlyWeight f = new CharacterFlyWeight(c);
map.put(c, f);
return f;
}
}
}
//******************************************************************************************
运行结果:
Character State = a String state = abcdefg
Character State = a String state = ABCDEFG
Character State = b String state = abcdefg
Character State = b String state = ABCDEFG
看到这里各位看官应该明白了吧,享元模式说穿了就是通过技术的手段让类中的一个对象或是多个对象,说成是(对象组可能更准确一些)实现共享,从而提高代码的效率.在这一点上有点类似单例模式,但请注意,享元模式可以允许多个实例的,这一点和单例不同.
<二>观察者模式(Observer Pattern)
观察者模式和上者不同,它属行为模式,也有人称之为(publish-subscribe)发布-订阅模式.或是(model-view)模型-视图模式,或是(Dependents)从属者模式.但是大多都还是称之为(observer)观察者模式,好了,不管如何对其如何称呼,其原理都是一样的.先让我们来看一下它的书面定义.
定义:Observer观察者模式定义了一种多对多的依赖关系,让多个观察者对象同时监听一个主题对象,这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自已.
在它里面一般有以下几个角色.
抽象主题角色
抽象观察者角色
具体主题角色
具体观察者角色.
下面我用一段代码来说明他们之间的关系.
//******************************************************************************************
package test;
import java.util.Enumeration;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
//实例化电影的主题对象.
Subject sub = new MovieSubject();
//实例化观察者工人和医生组.
Observer group1 = new Work();
Observer group2 = new Doctor();
//登记工人组和医生组
sub.attach(group1);
sub.attach(group2);
//通知已登记组
sub.notifyObserver();
}
}
// 抽象主题对象
interface Subject {
// 登记一个对象
public void attach(Observer observer);
// 删除一个已登记对象
public void detach(Observer observer);
//通知所有登记对象
void notifyObserver();
}
// 抽象观察者对象
interface Observer {
public void update();
}
// 具体主题对象
class MovieSubject implements Subject {
//主要实现此行为的一个非常重要的变量.也就是说它是观察者核心中的核心.
private Vector observersVector = new Vector();
public void attach(Observer observer) {
// TODO Auto-generated method stub
observersVector.addElement(observer);
}
public void detach(Observer observer) {
// TODO Auto-generated method stub
observersVector.remove(observer);
}
public void notifyObserver() {
// TODO Auto-generated method stub
//请注意下面的这句,是一个聚集的拷贝.
Enumeration enumer = ((Vector) observersVector.clone()).elements();
while (enumer.hasMoreElements()) {
((Observer) enumer.nextElement()).update();
}
}
}
// 具体观察者对象
class Work implements Observer {
public void update() {
// TODO Auto-generated method stub
System.out.println("已通知到工人");
}
}
// 具体观察者对象
class Doctor implements Observer {
public void update() {
// TODO Auto-generated method stub
System.out.println("已通知到医生");
}
}
//******************************************************************************************
运行结果:
已通知到工人
已通知到医生
先介绍一下Vector这个类:
Vector
类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector
的大小可以根据需要增大或缩小,以适应创建 Vector
后进行添加或移除项的操作。
在这里我们又看出来一个问题: 也就是说attach(),detach(), notifyObserver()实际上在每一个具体主题对象都会出现(比如说出现的现在不是电影主题对象,而是电视剧主题对象),重复的代码都更多一样,要做到最好的复用在此可能使用虚类可能比接口更合适.让我们来看一下下面这段代码.
//******************************************************************************************
package test;
import java.util.Enumeration;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
// 实例化电影的主题对象.
Subject sub = new MovieSubject();
// 实例化观察者工人和医生组.
Observer group1 = new Work();
Observer group2 = new Doctor();
// 登记工人组和医生组
sub.attach(group1);
sub.attach(group2);
// 通知已登记组
sub.notifyObserver();
}
}
// 抽象主题对象(改为虚类)
abstract class Subject {
// 登记一个对象
private Vector observersVector = new Vector();
public void attach(Observer observer) {
// TODO Auto-generated method stub
observersVector.addElement(observer);
}
// 删除一个已登记对象
public void detach(Observer observer) {
// TODO Auto-generated method stub
observersVector.remove(observer);
}
// 通知所有登记对象
public void notifyObserver() {
// TODO Auto-generated method stub
Enumeration enumer = ((Vector) observersVector.clone()).elements();
while (enumer.hasMoreElements()) {
((Observer) enumer.nextElement()).update();
}
}
}
// 抽象观察者对象
interface Observer {
public void update();
}
// 具体主题对象
class MovieSubject extends Subject {
//在此你可以加一些仅属于MovieSubject的一些字段或方法.如影片名.时间,地点,影片状态等等.
}
// 具体观察者对象
class Work implements Observer {
public void update() {
// TODO Auto-generated method stub
System.out.println("已通知到工人");
}
}
// 具体观察者对象
class Doctor implements Observer {
public void update() {
// TODO Auto-generated method stub
System.out.println("已通知到医生");
}
}
//******************************************************************************************
输出结果:
已通知到工人
已通知到医生
在以上的代码中.我们不难发现它是使用一个(Vector)聚集来实现此种来通知所有的具体观察者对象的.当然在java世界中很多类也使用此模式,如ImageObserver.还有从AWT1.1开始视窗系统事件的模型采用的也是此模式.
<三>合成模式(Composite)
也称之为部份-整体(part –whole)模式.
定义:
属于结构模式.它将对象组织到树结构中,可以用来描述整体与部份的关系.合成模式可以使客户端将单纯元素与复合元素同等看待.像windows里面的文件系统,就是一个典型的合成模式.想一想是不是同这几个组成的? “根目录->子目录(如果是文件则没有子级了)->文件”.
合成模式的三大构件角色.
抽象构件(Componet)角色
树叶构件(Leaf)角色
树枝(Composite)构件角色
理清楚思路后,先让我们来看下面这段代码:
//******************************************************************************************
package test;
import java.util.Enumeration;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
Branch root = new Branch();
Branch branch1 = new Branch();
Leaf leaf1 = new Leaf();
// 第一级
root.add(leaf1);
root.add(branch1);
Branch branch2 = new Branch();
Leaf leaf2 = new Leaf();
// 第二级
branch1.add(branch2);
branch1.add(leaf2);
// root.remove(leaf1);
root.doSomething();
}
}
// 抽象构件
interface Component {
Branch getMyself();
void doSomething();
}
// 树枝构件
class Branch implements Component {
//构建的核心.
private Vector vector = new Vector();
public void doSomething() {
// TODO Auto-generated method stub
Enumeration enumer = vector.elements();
while (enumer.hasMoreElements()) {
((Component) enumer.nextElement()).doSomething();
}
System.out.println("树枝");
}
public Branch getMyself() {
// TODO Auto-generated method stub
return this;
}
public void add(Component c) {
vector.addElement(c);
}
public void remove(Component c) {
vector.removeElement(c);
}
}
// 树叶构件
class Leaf implements Component {
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("树叶");
}
public Branch getMyself() {
// TODO Auto-generated method stub
return null;
}
}
//******************************************************************************************
结果输出:
树叶
树枝
树叶
树枝
树枝
结构比较简单,但相对来说可以说合成模式比以上几个模式都难一些,理解起来会比较混乱,让我们一起来理清这个思路就好了.首先来看这个答案为什么会这样呢?其实把它理解为一个树结构就好.其结构如下:
root -> leaf1 , branch1,而branch1 -> branch2,leaf2 //******************************************************************************************
//******************************************************************************************
这个结构有点类似于递归,程序先输出树叶这个大家应该没有什么异议,然后就有点类似于程序里面的递归了(只要里面包含树结构,则由下至上的输出),由于程序里面branch1包括branch2,leaf2,所以要欲输出branch1,则要先输出 branch2,leaf2,然后再输出branch1,最后再输出root.所以答案为:树叶, 树枝, 树叶, 树枝, 树枝.这一个大家应该明白了吧!呵呵.
以上的这个例子属合成模式中的安全式合成模式.在还有一种合成模式称之为透明式合成模式.下面我们就来看一下什么是透明式合成模式.