您的位置:首页 > 其它

设计模式之我见

2016-01-26 11:55 288 查看
本文参考sunny老师的系列博客:http://blog.csdn.net/lovelion/article/details/17517213

本文概述简单工厂模式+GOF23种设计模式的一些理解

设计模式之我见

面向接口编程

将代码中多变或者业务中多变的维度抽象封装

善于利用类的关联组合

合理增加交互中转类利于解耦

通常设计模式分为以下三种:

创建型模式:关注对象创建的优化,封装了对象的创建过程已解决创建对象中关于效率、可扩展、低耦合、隐藏过程细节等问题。

结构型模式:关注的类之间的结构的优化,很多时候通过增加中间类的形式极大的提高了现有业务的扩展性、封装性(处理类或对象的组合)。

行为型模式:关注的是类中行为的优化,如迭代器模式针对访问一个聚合对象元素、中介者模式针对多个对象之间的相互调用(对类或对象怎样交互和怎样分配职责进行描述)。

简单工厂模式(适用于类的创建比较简单且其创建没有额外的依赖操作的仅针对一个继承链的类创建,可对比工厂模式)

作用

用于统一管理继承类链中类的创建

通过工厂类隔离了类创建的细节

减少了大量的散落的类创建代码—利于类创建的解耦,如类创建的参数(构造方法改变)更改不至于把项目中的所有创建该对象的代码都改一遍,只要改工厂类的相应的创建对象的代码就行了。

结构图



改进

对于工厂类中传入的参数应通过static final修饰的变量统一管理(传入的参数不同对于创建相应的类)

将工厂类整合到创建类的接口抽象父类中,即在抽象父类中增加一个静态的创建方法(工厂类的方法),这种形式更加简化。

代码示例

abstract class Product {
//所有产品类的公共业务方法
public void methodSame() {
//公共方法的实现
}

//声明抽象业务方法
public abstract void methodDiff();
}


class ConcreteProduct extends Product {
//实现业务方法
public void methodDiff() {
//业务方法的实现
}
}


class Factory {
//静态工厂方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalsIgnoreCase("A")) {
product = new ConcreteProductA();
//初始化设置product
}
else if (arg.equalsIgnoreCase("B")) {
product = new ConcreteProductB();
//初始化设置product
}
return product;
}
}


class Client {
public static void main(String args[]) {
Product product;
product = Factory.getProduct("A"); //通过工厂类创建产品对象
product.methodSame();
product.methodDiff();
}
}


工厂模式(适用于类的创建比较复杂例如有多种参数的构造方法或其创建没有额外的依赖操作,在简单工厂的基础上维持了工厂类的继承链)

特性

简单工厂模式应对复杂类创建的进一步强化,也更加复杂

引入了工厂类的继承链,则工厂中的创建方法不能为静态的

由于类创建的复杂性,为每个类的创建引入一个新的工厂类,可针对该类不同参数的创建在工厂类中提供相应的重载方法

结构图



进化

可以直接在工厂类中定义类的业务方法,业务方法中包含了创建对象的代码,这样就进一步隐藏了简化了调用流程

代码示例

interface Factory {
public Product factoryMethod();
}


class ConcreteFactory implements Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}


……
Factory factory;
factory = new ConcreteFactory(); //可通过配置文件实现
Product product;
product = factory.factoryMethod();
……


抽象工厂模式(与工厂模式类似,不过其针对的是一个产品族,即每个具体的工厂类可以创建一个产品族即多个分类到一起的产品的实例)

特性

针对产品族扩展的产物

结构图



缺点

若要在产品族里增加一个产品,工厂类的改动会很大,但是在遇到增加产品族的需求上,我们只要增加一个该产品族的工厂类就行了

代码示例

abstract class AbstractFactory {
public abstract AbstractProductA createProductA(); //工厂方法一
public abstract AbstractProductB createProductB(); //工厂方法二
……
}


class ConcreteFactory1 extends AbstractFactory {
//工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}

//工厂方法二
public AbstractProductB createProductB() {
return new ConcreteProductB1();
}

……
}


单例模式

懒汉式:
class LazySingleton {
private volatile static LazySingleton instance = null;

private LazySingleton() { }

public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}


饿汉式:
class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() { }

public static EagerSingleton getInstance() {
return instance;
}
}


另一种更好的实现:
饿汉式单例类不能实现延迟加载,不管将来用不用始终占据内存;懒汉式单例类线程安全控制烦琐,而且性能受影响。可见,无论是饿汉式单例还是懒汉式单例都存在这样那样的问题
//Initialization on Demand Holder
class Singleton {
private Singleton() {
}

private static class HolderClass {
private final static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return HolderClass.instance;
}

public static void main(String args[]) {
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
}
由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。
通过使用IoDH(Initialization Demand Holder (IoDH)),我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式


原型模式

特性

如Java的Object对象提供的clone方法(该方法仅支持浅克隆,若成员变量是一个地址引用则直接复制该地址值,等于通过clone得到的对象和原对象的该成员变量引用的是同一段内存)就是用的原型模式,即在已有的对象基础上通过复制该对象的属性得到一个新的对象

通过原型管理器管理原型类,并获取相应的克隆类

结构图



//使用序列化技术实现深克隆
public WeeklyLog deepClone() throws  IOException, ClassNotFoundException, OptionalDataException
{
//将对象写入流中
ByteArrayOutputStream bao=new  ByteArrayOutputStream();
ObjectOutputStream oos=new  ObjectOutputStream(bao);
oos.writeObject(this);

//将对象从流中取出
ByteArrayInputStream bis=new  ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois=new  ObjectInputStream(bis);
return  (WeeklyLog)ois.readObject();
}


//原型管理器(使用饿汉式单例实现)
class  PrototypeManager
{
//定义一个Hashtable,用于存储原型对象
private Hashtable ht=new Hashtable();
private static PrototypeManager pm =  new PrototypeManager();

//为Hashtable增加公文对象
private  PrototypeManager()
{
ht.put("far",new  FAR());
ht.put("srs",new  SRS());
}

//增加新的公文对象
public void addOfficialDocument(String  key,OfficialDocument doc)
{
ht.put(key,doc);
}

//通过浅克隆获取新的公文对象
public OfficialDocument  getOfficialDocument(String key)
{
return  ((OfficialDocument)ht.get(key)).clone();
}

public static PrototypeManager  getPrototypeManager()
{
return pm;
}
}


建造者模式(复杂对象的组装与创建)

特性

建造者提供配置对象属性的抽象方法和生成最终对象的方法,具体建造者配置生产具体的最终商品(建造者的继承链),必要的时候可以多加一个控制类来控制建造者生成最终的产品。

结构图



进化

可以省略控制建造者的控制类直接在建造者中完成装配和建造的流程

可以在建造者中增加一些钩子方法(如增加一个人是否光头的方法,在建造的过程中通过这个方法来判断是否为其添加头发)

代码示例

class Product  {
private  String partA; //定义部件,部件可以是任意类型,包括值类型和引用类型
private  String partB;
private  String partC;
//partA的Getter方法和Setter方法省略
//partB的Getter方法和Setter方法省略
//partC的Getter方法和Setter方法省略
}


abstract class Builder {
//创建产品对象
protected  Product product=new Product();

public  abstract void buildPartA();
public  abstract void buildPartB();
public  abstract void buildPartC();

//返回产品对象
public  Product getResult() {
return  product;
}
}


class Director {
private  Builder builder;

public  Director(Builder builder) {
this.builder=builder;
}

public  void setBuilder(Builder builder) {
this.builder=builer;
}

//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}


……
Builder  builder = new ConcreteBuilder(); //可通过配置文件实现
Director director = new  Director(builder);
Product product = director.construct();
……


适配器模式(不同接口的融合、不暴露细节的情况下将第三方功能的整合融入)

适配器模式通常用于现有系统与第三方产品功能的集成,采用增加适配器的方式将第三方类集成到系统中。

特性

对象适配器:通过一个适配器类(保留功能类的引用然后在适配器的方法调用整合这些引用类的方法功能)在实现父类的抽象方法过程中调用了其它一些功能,就相当于将适配器接口的方法和关联的功能类的方法适配到一起。

缺省适配器:不是所有父类接口的方法都要实现,利用适配器为不需要实现的方法提供空实现需要实现的方法仍然抽象。

类适配器:适配器不仅实现目标接口,还要继承适配者从而直接调用适配者方法。

例子

//抽象成绩操作类:目标接口
interface ScoreOperation {
public int[] sort(int array[]); //成绩排序
public int search(int array[],int key); //成绩查找
}

//快速排序类:适配者
class QuickSort {
public int[] quickSort(int array[]) {
sort(array,0,array.length-1);
return array;
}

public void sort(int array[],int p, int r) {
int q=0;
if(p<r) {
q=partition(array,p,r);
sort(array,p,q-1);
sort(array,q+1,r);
}
}

public int partition(int[] a, int p, int r) {
int x=a[r];
int j=p-1;
for (int i=p;i<=r-1;i++) {
if (a[i]<=x) {
j++;
swap(a,j,i);
}
}
swap(a,j+1,r);
return j+1;
}

public void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}

//二分查找类:适配者
class BinarySearch {
public int binarySearch(int array[],int key) {
int low = 0;
int high = array.length -1;
while(low <= high) {
int mid = (low + high) / 2;
int midVal = array[mid];
if(midVal < key) {
low = mid +1;
}
else if (midVal > key) {
high = mid -1;
}
else {
return 1; //找到元素返回1
}
}
return -1;  //未找到元素返回-1
}
}

//操作适配器:适配器
class OperationAdapter implements ScoreOperation {
private QuickSort sortObj; //定义适配者QuickSort对象
private BinarySearch searchObj; //定义适配者BinarySearch对象

public OperationAdapter() {
sortObj = new QuickSort();
searchObj = new BinarySearch();
}

public int[] sort(int array[]) {
return sortObj.quickSort(array); //调用适配者类QuickSort的排序方法
}

public int search(int array[],int key) {
return searchObj.binarySearch(array,key); //调用适配者类BinarySearch的查找方法
}
}

<?xml version="1.0"?>
<config>
<className>OperationAdapter</className>
</config>


class Client {
public static void main(String args[]) {
ScoreOperation operation;  //针对抽象目标接口编程
operation = (ScoreOperation)XMLUtil.getBean(); //读取配置文件,反射生成对象
int scores[] = {84,76,50,69,90,91,88,96}; //定义成绩数组
int result[];
int score;

System.out.println("成绩排序结果:");
result = operation.sort(scores);

//遍历输出成绩
for(int i : scores) {
System.out.print(i + ",");
}
System.out.println();

System.out.println("查找成绩90:");
score = operation.search(result,90);
if (score != -1) {
System.out.println("找到成绩90。");
}
else {
System.out.println("没有找到成绩90。");
}

System.out.println("查找成绩92:");
score = operation.search(result,92);
if (score != -1) {
System.out.println("找到成绩92。");
}
else {
System.out.println("没有找到成绩92。");
}
}
}


桥接模式(一件事物的表现依赖于多个独立变化的维度并且各个变化维度纠缠,用于将各个维度分离解耦、简化)

例子

各种类型图片在不同操作系统的显示有两个独立维度的变化

图片的类型

操作系统的类型

下面是其构想的类结构图,可以明显看出太复杂、臃肿,且不利于扩展。

利用桥接模式得出的类结构图:

* 将操作系统的变化独立出来形成一个继承链

* 本身的图片类型继承链通过增加一个设置操作系统的方法关联操作系统的继承链

* 最终实现了更好的解决方法

代码

//像素矩阵类:辅助类,各种格式的文件最终都被转化为像素矩阵,不同的操作系统提供不同的方式显示像素矩阵
class Matrix {
//此处代码省略
}

//抽象图像类:抽象类
abstract class Image {
protected ImageImp imp;

public void setImageImp(ImageImp imp) {
this.imp = imp;
}

public abstract void parseFile(String fileName);
}

//抽象操作系统实现类:实现类接口
interface ImageImp {
public void doPaint(Matrix m);  //显示像素矩阵m
}

//Windows操作系统实现类:具体实现类
class WindowsImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Windows系统的绘制函数绘制像素矩阵
System.out.print("在Windows操作系统中显示图像:");
}
}

//Linux操作系统实现类:具体实现类
class LinuxImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Linux系统的绘制函数绘制像素矩阵
System.out.print("在Linux操作系统中显示图像:");
}
}

//Unix操作系统实现类:具体实现类
class UnixImp implements ImageImp {
public void doPaint(Matrix m) {
//调用Unix系统的绘制函数绘制像素矩阵
System.out.print("在Unix操作系统中显示图像:");
}
}

//JPG格式图像:扩充抽象类
class JPGImage extends Image {
public void parseFile(String fileName) {
//模拟解析JPG文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为JPG。");
}
}

//PNG格式图像:扩充抽象类
class PNGImage extends Image {
public void parseFile(String fileName) {
//模拟解析PNG文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为PNG。");
}
}

//BMP格式图像:扩充抽象类
class BMPImage extends Image {
public void parseFile(String fileName) {
//模拟解析BMP文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为BMP。");
}
}

//GIF格式图像:扩充抽象类
class GIFImage extends Image {
public void parseFile(String fileName) {
//模拟解析GIF文件并获得一个像素矩阵对象m;
Matrix m = new Matrix();
imp.doPaint(m);
System.out.println(fileName + ",格式为GIF。");
}
}

<?xml version="1.0"?>
<config>
<!--RefinedAbstraction-->
<className>JPGImage</className>
<!--ConcreteImplementor-->
<className>WindowsImp</className>
</config>

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
public class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean(String args) {
try {
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));
NodeList nl=null;
Node classNode=null;
String cName=null;
nl = doc.getElementsByTagName("className");

if(args.equals("image")) {
//获取第一个包含类名的节点,即扩充抽象类
classNode=nl.item(0).getFirstChild();

}
else if(args.equals("os")) {
//获取第二个包含类名的节点,即具体实现类
classNode=nl.item(1).getFirstChild();
}

cName=classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}

class Client {
public static void main(String args[]) {
Image image;
ImageImp imp;
image = (Image)XMLUtil.getBean("image");
imp = (ImageImp)XMLUtil.getBean("os");
image.setImageImp(imp);
image.parseFile("小龙女");
}
}


联想

桥接模式和适配器模式用于设计的不同阶段,桥接模式用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化;而在初步设计完成之后,当发现系统与已有类无法协同工作时,可以采用适配器模式。但有时候在设计初期也需要考虑适配器模式,特别是那些涉及到大量第三方应用接口的情况。

组合模式(针对树形结构的结构链具有相同业务方法的简单、解耦实现)

组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。

组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性

其类结构如下:

树形结构中有一层层的包含关系,但是拥有同样的业务方法

Componet表示树形结构中的一环,这样使得树形结构中不管是根节点还是叶结点都具有一致性

Componet定义了添加、删除、获取子元素的方法为了体现容器节点表现整体-部分关系。

代码例子:

设计一个对文件的杀毒模型

import java.util.*;

//抽象文件类:抽象构件
abstract class AbstractFile {
public abstract void add(AbstractFile file);
public abstract void remove(AbstractFile file);
public abstract AbstractFile getChild(int i);
public abstract void killVirus();
}

//图像文件类:叶子构件
class ImageFile extends AbstractFile {
private String name;

public ImageFile(String name) {
this.name = name;
}

public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}

public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}

public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}

public void killVirus() {
//模拟杀毒
System.out.println("----对图像文件'" + name + "'进行杀毒");
}
}

//文本文件类:叶子构件
class TextFile extends AbstractFile {
private String name;

public TextFile(String name) {
this.name = name;
}

public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}

public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}

public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}

public void killVirus() {
//模拟杀毒
System.out.println("----对文本文件'" + name + "'进行杀毒");
}
}

//视频文件类:叶子构件
class VideoFile extends AbstractFile {
private String name;

public VideoFile(String name) {
this.name = name;
}

public void add(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}

public void remove(AbstractFile file) {
System.out.println("对不起,不支持该方法!");
}

public AbstractFile getChild(int i) {
System.out.println("对不起,不支持该方法!");
return null;
}

public void killVirus() {
//模拟杀毒
System.out.println("----对视频文件'" + name + "'进行杀毒");
}
}

//文件夹类:容器构件
class Folder extends AbstractFile {
//定义集合fileList,用于存储AbstractFile类型的成员
private ArrayList<AbstractFile> fileList=new ArrayList<AbstractFile>();
private String name;

public Folder(String name) {
this.name = name;
}

public void add(AbstractFile file) {
fileList.add(file);
}

public void remove(AbstractFile file) {
fileList.remove(file);
}

public AbstractFile getChild(int i) {
return (AbstractFile)fileList.get(i);
}

public void killVirus() {
System.out.println("****对文件夹'" + name + "'进行杀毒");  //模拟杀毒

//递归调用成员构件的killVirus()方法
for(Object obj : fileList) {
((AbstractFile)obj).killVirus();
}
}
}

class Client {
public static void main(String args[]) {
//针对抽象构件编程
AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;

folder1 = new Folder("Sunny的资料");
folder2 = new Folder("图像文件");
folder3 = new Folder("文本文件");
folder4 = new Folder("视频文件");

file1 = new ImageFile("小龙女.jpg");
file2 = new ImageFile("张无忌.gif");
file3 = new TextFile("九阴真经.txt");
file4 = new TextFile("葵花宝典.doc");
file5 = new VideoFile("笑傲江湖.rmvb");

folder2.add(file1);
folder2.add(file2);
folder3.add(file3);
folder3.add(file4);
folder4.add(file5);
folder1.add(folder2);
folder1.add(folder3);
folder1.add(folder4);

//从“Sunny的资料”节点开始进行杀毒操作
folder1.killVirus();
}
}


安全组合模式:

即抽象父类不声明容器节点的管理子节点的方法

容器类的实现类中添加相应的管理子节点的方法

这样的缺点是容器类节点不能针对抽象类编程,因为它要调用管理子节点的方法

优点是非容器的节点不能调用管理子节点的方法而报告错误。

透明组合模式:

对比安全组合模式,非容器节点在调用管理子节点的方法时会报告错误。但可以针对抽象类编程。

装饰模式(在原有业务上添加额外的职责操作,比如光脚走路和穿鞋走路的区别,穿鞋这个就是额外的装饰操作)

特性

在继承原有业务方法的基础上装饰类通过传入具体业务实现类(通过构造方法参数传入或新增函数参数传入),从而可以在实现业务的过程中一方面调用传入业务实现类的方法,而外的可以新增一些我们自己添加的操作

和适配器模式有点像,只不过适配器传入的对象参数的接口和适配器继承的接口不同(装饰模式参数和本身继承的同一个接口),只是起到兼容两个接口的目的,而没有额外操作的部分。

透明装饰模式

完全针对接口编程

半透明装饰模式

装饰类需要单独调用新增的业务方法(该业务方法不在父类中声明)

类结构

代码示例

图形界面组件库的设计:

应用了装饰模式:

//抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大量的简化
abstract class Component
{
public  abstract void display();
}

//窗体类:具体构件类
class Window extends Component
{
public  void display()
{
System.out.println("显示窗体!");
}
}

//文本框类:具体构件类
class TextBox extends Component
{
public  void display()
{
System.out.println("显示文本框!");
}
}

//列表框类:具体构件类
class ListBox extends Component
{
public  void display()
{
System.out.println("显示列表框!");
}
}

//构件装饰类:抽象装饰类
class ComponentDecorator extends Component
{
private Component component;  //维持对抽象构件类型对象的引用

public ComponentDecorator(Component  component)  //注入抽象构件类型的对象
{
this.component = component;
}

public void display()
{
component.display();
}
}

//滚动条装饰类:具体装饰类
class ScrollBarDecorator extends  ComponentDecorator
{
public ScrollBarDecorator(Component  component)
{
super(component);
}

public void display()
{
this.setScrollBar();
super.display();
}

public  void setScrollBar()
{
System.out.println("为构件增加滚动条!");
}
}

//黑色边框装饰类:具体装饰类
class BlackBorderDecorator extends  ComponentDecorator
{
public BlackBorderDecorator(Component  component)
{
super(component);
}

public void display()
{
this.setBlackBorder();
super.display();
}

public  void setBlackBorder()
{
System.out.println("为构件增加黑色边框!");
}
}


class Client

{

public static void main(String args[])

{

Component component,componentSB,componentBB; //全部使用抽象构件定义

component = new Window();

componentSB = new ScrollBarDecorator(component);

componentBB = new BlackBorderDecorator(componentSB); //将装饰了一次之后的对象继续注入到另一个装饰类中,进行第二次装饰

componentBB.display();

}

}

联想

装饰模式和桥接模式表面上看都有几个变化维度,区别在于桥接模式的几个变化维度相互比较独立,其结构已经是固定的,而装饰模式的变化维度关联依赖性强,其结构是做加法运算

外观模式(当我们的意图需要设计到多个类的调用交互,我们可以创建一个拥有该意图业务方法的外观类,利用这个外观类来统一调度各个子系统的交互)

抽象外观类:

通过引入外观类的继承链使得子系统变化不至于大改外观类而可以通过新增一个外观类解决

应用外观模式的前后类比

享元模式(将大量用到且细粒度的对象放到享元池中共享,享元类的利用是内部状态和外部状态的有机结合)

单纯享元模式

复合享元模式(在单纯享元模式的基础上结合组合模式形成了复合享元类即单纯享元类的集合,只不过该复合享元类所包含的单纯享元类的外部状态都是相同的,内部状态可以不同)

例子:围棋

享元池内部状态相同的享元类的复用

import java.util.*;

//围棋棋子类:抽象享元类
abstract class IgoChessman {
public abstract String getColor();

public void display() {
System.out.println("棋子颜色:" + this.getColor());
}
}

//黑色棋子类:具体享元类
class BlackIgoChessman extends IgoChessman {
public String getColor() {
return "黑色";
}
}

//白色棋子类:具体享元类
class WhiteIgoChessman extends IgoChessman {
public String getColor() {
return "白色";
}
}

//围棋棋子工厂类:享元工厂类,使用单例模式进行设计
class IgoChessmanFactory {
private static IgoChessmanFactory instance = new IgoChessmanFactory();
private static Hashtable ht; //使用Hashtable来存储享元对象,充当享元池

private IgoChessmanFactory() {
ht = new Hashtable();
IgoChessman black,white;
black = new BlackIgoChessman();
ht.put("b",black);
white = new WhiteIgoChessman();
ht.put("w",white);
}

//返回享元工厂类的唯一实例
public static IgoChessmanFactory getInstance() {
return instance;
}

//通过key来获取存储在Hashtable中的享元对象
public static IgoChessman getIgoChessman(String color) {
return (IgoChessman)ht.get(color);
}
}

class Client {
public static void main(String args[]) {
IgoChessman black1,black2,black3,white1,white2;
IgoChessmanFactory factory;

//获取享元工厂对象
factory = IgoChessmanFactory.getInstance();

//通过享元工厂获取三颗黑子
black1 = factory.getIgoChessman("b");
black2 = factory.getIgoChessman("b");
black3 = factory.getIgoChessman("b");
System.out.println("判断两颗黑子是否相同:" + (black1==black2));

//通过享元工厂获取两颗白子
white1 = factory.getIgoChessman("w");
white2 = factory.getIgoChessman("w");
System.out.println("判断两颗白子是否相同:" + (white1==white2));

//显示棋子
black1.display();
black2.display();
black3.display();
white1.display();
white2.display();
}
}


在上面的基础上结合外部状态达成享元类在不同位置显示(将外部状态抽象成一个类,并将相应的外部状态实例作为参数传给享元类):

//坐标类:外部状态类
class Coordinates {
private int x;
private int y;

public Coordinates(int x,int y) {
this.x = x;
this.y = y;
}

public int getX() {
return this.x;
}

public void setX(int x) {
this.x = x;
}

public int getY() {
return this.y;
}

public void setY(int y) {
this.y = y;
}
}

//围棋棋子类:抽象享元类
abstract class IgoChessman {
public abstract String getColor();

public void display(Coordinates coord){
System.out.println("棋子颜色:" + this.getColor() + ",棋子位置:" + coord.getX() + "," + coord.getY() );
}
}

class Client {
public static void main(String args[]) {
IgoChessman black1,black2,black3,white1,white2;
IgoChessmanFactory factory;

//获取享元工厂对象
factory = IgoChessmanFactory.getInstance();

//通过享元工厂获取三颗黑子
black1 = factory.getIgoChessman("b");
black2 = factory.getIgoChessman("b");
black3 = factory.getIgoChessman("b");
System.out.println("判断两颗黑子是否相同:" + (black1==black2));

//通过享元工厂获取两颗白子
white1 = factory.getIgoChessman("w");
white2 = factory.getIgoChessman("w");
System.out.println("判断两颗白子是否相同:" + (white1==white2));

//显示棋子,同时设置棋子的坐标位置
black1.display(new Coordinates(1,2));
black2.display(new Coordinates(3,4));
black3.display(new Coordinates(1,3));
white1.display(new Coordinates(2,5));
white2.display(new Coordinates(2,4));
}
}


JDK类库中的String类使用了享元模式,我们通过如下代码来加以说明:

class Demo {

public static void main(String args[]) {

String str1 = “abcd”;

String str2 = “abcd”;

String str3 = “ab” + “cd”;

String str4 = “ab”;

str4 += “cd”;

//因为是同一个复合享元对象

System.out.println(str1 == str2);//true

//都是 “a”“b” “c”“d”四个单纯享元对象,故都是引用的同一个复合享元实例

System.out.println(str1 == str3);//true

// str4 += “cd”相当于复制了str4到新的一个对象(原型模式?)然后对新的对象操作添加 “c”“d”两个单纯享元对象

System.out.println(str1 == str4);//false

str2  += "e";


//将str2添加了一个单纯享元对象”e”,即复制str2到一个新的内存地址,并添加”e”,故和复合享元实例”abcd”的地址不同。

System.out.println(str1 == str2);//false

}

}

在Java语言中,如果每次执行类似String str1=”abcd”的操作时都创建一个新的字符串对象将导致内存开销很大,因此如果第一次创建了内容为”abcd”的字符串对象str1,下一次再创建内容相同>的字符串对象str2时会将它的引用指向”abcd”,不会重新分配内存空间,从而实现了”abcd”在内存中的共享。上述代码输出结果如下:

关于Java String类这种在修改享元对象时,先将原有对象复制一份,然后在新对象上再实施修改操作的机制称为“Copy On Write”

代理模式(隐藏真实业务类,并在调用真实业务类业务方法前后做需要的操作)

(1) 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。

(2) 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

(3) 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

(4) 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

(5) 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。

在这些常用的代理模式中,有些代理类的设计非常复杂,例如远程代理类,它封装了底层网络通信和对远程对象的调用,其实现较为复杂。

例子

查询工商记录并检查登录和写入日志的代理模式类结构

职责链模式

一个Handler的处理类继承链,职责链上的所有环节都继承这个抽象类

要创建一个链式的处理方式,就要使得Handler类中保存下一个处理类的引用,当符合条件是则交给下一个处理类Handler处理

纯的职责连模式(请求的链式处理)

个纯的职责链模式要求一个具体处理者对象只能在两个行为中选择一个:要么承担全部责任,要么将责任推给下家,不允许出现某一个具体处理者对象在承担了一

部分或全部责任后又将责任向下传递的情况。而且在纯的职责链模式中,要求一个请求必须被某一个处理者对象所接收,不能出现某个请求未被任何一个处理者对

象处理的情况。

不纯的职责连模式

在一个不纯的职责链模式中允许某个请求被一个具体处理者部分处理后再向下传递,或者一个具体处理者处理完某请求后其后继处理者可以继续处理该请求,

而且一个请求可以最终不被任何处理者对象所接收。

类结构

代码(订单审批链)

//采购单:请求类
class PurchaseRequest {
private double amount;  //采购金额
private int number;  //采购单编号
private String purpose;  //采购目的

public PurchaseRequest(double amount, int number, String purpose) {
this.amount = amount;
this.number = number;
this.purpose = purpose;
}

public void setAmount(double amount) {
this.amount = amount;
}

public double getAmount() {
return this.amount;
}

public void setNumber(int number) {
this.number = number;
}

public int getNumber() {
return this.number;
}

public void setPurpose(String purpose) {
this.purpose = purpose;
}

public String getPurpose() {
return this.purpose;
}
}

//审批者类:抽象处理者
abstract class Approver {
protected Approver successor; //定义后继对象
protected String name; //审批者姓名

public Approver(String name) {
this.name = name;
}

//设置后继者
public void setSuccessor(Approver successor) {
this.successor = successor;
}

//抽象请求处理方法
public abstract void processRequest(PurchaseRequest request);
}

//主任类:具体处理者
class Director extends Approver {
public Director(String name) {
super(name);
}

//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 50000) {
System.out.println("主任" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求
}
else {
this.successor.processRequest(request);  //转发请求
}
}
}

//副董事长类:具体处理者
class VicePresident extends Approver {
public VicePresident(String name) {
super(name);
}

//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 100000) {
System.out.println("副董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求
}
else {
this.successor.processRequest(request);  //转发请求
}
}
}

//董事长类:具体处理者
class President extends Approver {
public President(String name) {
super(name);
}

//具体请求处理方法
public void processRequest(PurchaseRequest request) {
if (request.getAmount() < 500000) {
System.out.println("董事长" + this.name + "审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");  //处理请求
}
else {
this.successor.processRequest(request);  //转发请求
}
}
}

//董事会类:具体处理者
class Congress extends Approver {
public Congress(String name) {
super(name);
}

//具体请求处理方法
public void processRequest(PurchaseRequest request) {
System.out.println("召开董事会审批采购单:" + request.getNumber() + ",金额:" + request.getAmount() + "元,采购目的:" + request.getPurpose() + "。");        //处理请求
}
}

class Client {
public static void main(String[] args) {
Approver wjzhang,gyang,jguo,meeting;
wjzhang = new Director("张无忌");
gyang = new VicePresident("杨过");
jguo = new President("郭靖");
meeting = new Congress("董事会");

//创建职责链
wjzhang.setSuccessor(gyang);
gyang.setSuccessor(jguo);
jguo.setSuccessor(meeting);

//创建采购单
PurchaseRequest pr1 = new PurchaseRequest(45000,10001,"购买倚天剑");
wjzhang.processRequest(pr1);

PurchaseRequest pr2 = new PurchaseRequest(60000,10002,"购买《葵花宝典》");
wjzhang.processRequest(pr2);

PurchaseRequest pr3 = new PurchaseRequest(160000,10003,"购买《金刚经》");
wjzhang.processRequest(pr3);

PurchaseRequest pr4 = new PurchaseRequest(800000,10004,"购买桃花岛");
wjzhang.processRequest(pr4);
}
}


命令模式(请求发送者与接收者解耦)

类结构

命令Command作为一个中转类,为了扩展其应该是一个继承链,维持一个统一的接口。

Invoker类相当于命令触发类,为了对命令接受者的解耦其应该持有Command的引用并有设置Command的方法。

Command的实例不应暴露真实的信号接收者(在Command创建的时候,相应的Receiver就该创建)

宏命令即命令模式和组合模式的结合:

代码模型:

class Invoker {
private Command command;

//构造注入
public Invoker(Command command) {
this.command = command;
}

//设值注入
public void setCommand(Command command) {
this.command = command;
}

//业务方法,用于调用命令类的execute()方法
public void call() {
command.execute();
}
}

class ConcreteCommand extends Command {
private Receiver receiver; //维持一个对请求接收者对象的引用

public void execute() {
receiver.action(); //调用请求接收者的业务处理方法action()
}
}

class Receiver {
public void action() {
//具体操作
}
}


联想

命令模式和观察者模式有几分相似,但是命令模式是由请求决定触发哪个接受者,观察者模式中观察者是被动被触发的。

观察者模式定义了一种一对多(多个观察者)的依赖关系,让多个观察者对象同时监听一个主题对象,这个主题对象在状态改变的时候,会通知所有观察者对像,使他们能够自动更新。

解释器模式(自定义语言的实现)

Sunny软件公司开发了一套简单的基于字符界面的格式化指令,可以根据输入的指令在字符界面中输出一些格式化内容,例如输入“LOOP 2 PRINT杨过 SPACE SPACE PRINT 小龙女 BREAK >END PRINT郭靖 SPACE SPACE PRINT 黄蓉”,将输出如下结果:

杨过 小龙女

杨过 小龙女

郭靖 黄蓉

其中关键词LOOP表示“循环”,后面的数字表示循环次数;PRINT表示“打印”,后面的字符串表示打印的内容;SPACE表示“空格”;BREAK表示“换行”;END表示“循环结束”。每一个关键词对

应一条命令,计算机程序将根据关键词执行相应的处理操作。

现使用解释器模式设计并实现该格式化指令的解释,对指令进行分析并调用相应的操作执行指令中每一条命令。

在解释器模式中,环境类Context用于存储解释器之外的一些全局信息,它通常作为参数被传递到所有表达式的解释方法interpret()中,可以在Context对象中存储和访问表达式解释器的状态,

向表达式解释器提供一些全局的、公共的数据,此外还可以在Context中增加一些所有表达式解释器都共有的功能,减轻解释器的职责。

import java.util.*;
//环境类:用于存储和操作需要解释的语句,在本实例中每一个需要解释的单词可以称为一个动作标记(Action Token)或命令
class Context {
private StringTokenizer tokenizer; //StringTokenizer类,用于将字符串分解为更小的字符串标记(Token),默认情况下以空格作为分隔符
private String currentToken; //当前字符串标记

public Context(String text) {
tokenizer = new StringTokenizer(text); //通过传入的指令字符串创建StringTokenizer对象
nextToken();
}

//返回下一个标记
public String nextToken() {
if (tokenizer.hasMoreTokens()) {
currentToken = tokenizer.nextToken();
}
else {
currentToken = null;
}
return currentToken;
}

//返回当前的标记
public String currentToken() {
return currentToken;
}

//跳过一个标记
public void skipToken(String token) {
if (!token.equals(currentToken)) {
System.err.println("错误提示:" + currentToken + "解释错误!");
}
nextToken();
}

//如果当前的标记是一个数字,则返回对应的数值
public int currentNumber() {
int number = 0;
try{
number = Integer.parseInt(currentToken); //将字符串转换为整数
}
catch(NumberFormatException e) {
System.err.println("错误提示:" + e);
}
return number;
}
}

//抽象节点类:抽象表达式
abstract class Node {
public abstract void interpret(Context text); //声明一个方法用于解释语句
public abstract void execute(); //声明一个方法用于执行标记对应的命令
}

//表达式节点类:非终结符表达式
class ExpressionNode extends Node {
private ArrayList<Node> list = new ArrayList<Node>(); //定义一个集合用于存储多条命令

public void interpret(Context context) {
//循环处理Context中的标记
while (true){
//如果已经没有任何标记,则退出解释
if (context.currentToken() == null) {
break;
}
//如果标记为END,则不解释END并结束本次解释过程,可以继续之后的解释
else if (context.currentToken().equals("END")) {
context.skipToken("END");
break;
}
//如果为其他标记,则解释标记并将其加入命令集合
else {
Node commandNode = new CommandNode();
commandNode.interpret(context);
list.add(commandNode);
}
}
}

//循环执行命令集合中的每一条命令
public void execute() {
Iterator iterator = list.iterator();
while (iterator.hasNext()){
((Node)iterator.next()).execute();
}
}
}

//语句命令节点类:非终结符表达式
class CommandNode extends Node {
private Node node;

public void interpret(Context context) {
//处理LOOP循环命令
if (context.currentToken().equals("LOOP")) {
node = new LoopCommandNode();
node.interpret(context);
}
//处理其他基本命令
else {
node = new PrimitiveCommandNode();
node.interpret(context);
}
}

public void execute() {
node.execute();
}
}

//循环命令节点类:非终结符表达式
class LoopCommandNode extends Node {
private int number; //循环次数
private Node commandNode; //循环语句中的表达式

//解释循环命令
public void interpret(Context context) {
context.skipToken("LOOP");
number = context.currentNumber();
context.nextToken();
commandNode = new ExpressionNode(); //循环语句中的表达式
commandNode.interpret(context);
}

public void execute() {
for (int i=0;i<number;i++)
commandNode.execute();
}
}

//基本命令节点类:终结符表达式
class PrimitiveCommandNode extends Node {
private String name;
private String text;

//解释基本命令
public void interpret(Context context) {
name = context.currentToken();
context.skipToken(name);
if (!name.equals("PRINT") && !name.equals("BREAK") && !name.equals ("SPACE")){
System.err.println("非法命令!");
}
if (name.equals("PRINT")){
text = context.currentToken();
context.nextToken();
}
}

public void execute(){
if (name.equals("PRINT"))
System.out.print(text);
else if (name.equals("SPACE"))
System.out.print(" ");
else if (name.equals("BREAK"))
System.out.println();
}
}

class Client{
public static void main(String[] args){
String text = "LOOP 2 PRINT 杨过 SPACE SPACE PRINT 小龙女 BREAK END PRINT 郭靖 SPACE SPACE PRINT 黄蓉";
Context context = new Context(text);

Node node = new ExpressionNode();
node.interpret(context);
node.execute();
}
}


迭代器模式(遍历聚合对象中的元素)

需求

在软件开发中,也存在大量类似电视机一样的类,它们可以存储多个成员对象(元素),这些类通常称为聚合类(Aggregate Classes),对应的对象称为聚合对象。为了更加方便地操作这些聚合对象,同时可以很灵活地为聚合对象增加不同的遍历方法,我们也需要类似电视机遥控器一样的角色,可以访问一个聚合对象中的元素但又不需要暴露它的内部结构。

类结构

例子:

代码:

//在本实例中,为了详细说明自定义迭代器的实现过程,我们没有使用JDK中内置的迭代器,事实上,JDK内置迭代器已经实现了对一个List对象的正向遍历
import java.util.*;

//抽象聚合类
abstract class AbstractObjectList {
protected List<Object> objects = new ArrayList<Object>();

public AbstractObjectList(List objects) {
this.objects = objects;
}

public void addObject(Object obj) {
this.objects.add(obj);
}

public void removeObject(Object obj) {
this.objects.remove(obj);
}

public List getObjects() {
return this.objects;
}

//声明创建迭代器对象的抽象工厂方法
public abstract AbstractIterator createIterator();
}

//商品数据类:具体聚合类
class ProductList extends AbstractObjectList {
public ProductList(List products) {
super(products);
}

//实现创建迭代器对象的具体工厂方法
public AbstractIterator createIterator() {
return new ProductIterator(this);
}
}

//抽象迭代器
interface AbstractIterator {
public void next(); //移至下一个元素
public boolean isLast(); //判断是否为最后一个元素
public void previous(); //移至上一个元素
public boolean isFirst(); //判断是否为第一个元素
public Object getNextItem(); //获取下一个元素
public Object getPreviousItem(); //获取上一个元素
}

//商品迭代器:具体迭代器
class ProductIterator implements AbstractIterator {
private ProductList productList;
private List products;
private int cursor1; //定义一个游标,用于记录正向遍历的位置
private int cursor2; //定义一个游标,用于记录逆向遍历的位置

public ProductIterator(ProductList list) {
this.productList = list;
this.products = list.getObjects(); //获取集合对象
cursor1 = 0; //设置正向遍历游标的初始值
cursor2 = products.size() -1; //设置逆向遍历游标的初始值
}

public void next() {
if(cursor1 < products.size()) {
cursor1++;
}
}

public boolean isLast() {
return (cursor1 == products.size());
}

public void previous() {
if (cursor2 > -1) {
cursor2--;
}
}

public boolean isFirst() {
return (cursor2 == -1);
}

public Object getNextItem() {
return products.get(cursor1);
}

public Object getPreviousItem() {
return products.get(cursor2);
}
}

class Client {
public static void main(String args[]) {
List products = new ArrayList();
products.add("倚天剑");
products.add("屠龙刀");
products.add("断肠草");
products.add("葵花宝典");
products.add("四十二章经");

AbstractObjectList list;
AbstractIterator iterator;

list = new ProductList(products); //创建聚合对象
iterator = list.createIterator();   //创建迭代器对象

System.out.println("正向遍历:");
while(!iterator.isLast()) {
System.out.print(iterator.getNextItem() + ",");
iterator.next();
}
System.out.println();
System.out.println("-----------------------------");
System.out.println("逆向遍历:");
while(!iterator.isFirst()) {
System.out.print(iterator.getPreviousItem() + ",");
iterator.previous();
}
}
}


使用内部类实现迭代器,JDK中的迭代器类就是通过这种方法来实现的

可通过增加不同类型的迭代器来满足不同的遍历需求,如JDK中增加了ListIterator来满足反向遍历的需求:

中介者模式(协调多个对象之间的交互)

协调界面组件对象之间的复杂交互关系,Sunny公司开发人员使用中介者模式来设计客户信息管理窗口(解决各个组件的信息联动问题):

代码:

//抽象中介者
abstract class Mediator {
public abstract void componentChanged(Component c);
}

//具体中介者
class ConcreteMediator extends Mediator {
//维持对各个同事对象的引用
public Button addButton;
public List list;
public TextBox userNameTextBox;
public ComboBox cb;

//封装同事对象之间的交互
public void componentChanged(Component c) {
//单击按钮
if(c == addButton) {
System.out.println("--单击增加按钮--");
list.update();
cb.update();
userNameTextBox.update();
}
//从列表框选择客户
else if(c == list) {
System.out.println("--从列表框选择客户--");
cb.select();
userNameTextBox.setText();
}
//从组合框选择客户
else if(c == cb) {
System.out.println("--从组合框选择客户--");
cb.select();
userNameTextBox.setText();
}
}
}

//抽象组件类:抽象同事类
abstract class Component {
protected Mediator mediator;

public void setMediator(Mediator mediator) {
this.mediator = mediator;
}

//转发调用
public void changed() {
mediator.componentChanged(this);
}

public abstract void update();
}

//按钮类:具体同事类
class Button extends Component {
public void update() {
//按钮不产生交互
}
}

//列表框类:具体同事类
class List extends Component {
public void update() {
System.out.println("列表框增加一项:张无忌。");
}

public void select() {
System.out.println("列表框选中项:小龙女。");
}
}

//组合框类:具体同事类
class ComboBox extends Component {
public void update() {
System.out.println("组合框增加一项:张无忌。");
}

public void select() {
System.out.println("组合框选中项:小龙女。");
}
}

//文本框类:具体同事类
class TextBox extends Component {
public void update() {
System.out.println("客户信息增加成功后文本框清空。");
}

public void setText() {
System.out.println("文本框显示:小龙女。");
}
}

class Client {
public static void main(String args[]) {
//定义中介者对象
ConcreteMediator mediator;
mediator = new ConcreteMediator();

//定义同事对象
Button addBT = new Button();
List list = new List();
ComboBox cb = new ComboBox();
TextBox userNameTB = new TextBox();

addBT.setMediator(mediator);
list.setMediator(mediator);
cb.setMediator(mediator);
userNameTB.setMediator(mediator);

mediator.addButton = addBT;
mediator.list = list;
mediator.cb = cb;
mediator.userNameTextBox = userNameTB;

addBT.changed();
System.out.println("-----------------------------");
list.changed();
}
}


备忘录模式(撤销功能的实现)

特性

通过一个备忘录类来保存需要备份的类的状态

可以增加一个备忘录管理类用于管理(保存或者获取备忘录)一个或多个备忘录实例

需要备份的类需要有备份和恢复的方法来激发

例子(棋子的悔棋)

//象棋棋子类:原发器
class Chessman {
private String label;
private int x;
private int y;

public Chessman(String label,int x,int y) {
this.label = label;
this.x = x;
this.y = y;
}

public void setLabel(String label) {
this.label = label;
}

public void setX(int x) {
this.x = x;
}

public void setY(int y) {
this.y = y;
}

public String getLabel() {
return (this.label);
}

public int getX() {
return (this.x);
}

public int getY() {
return (this.y);
}

//保存状态
public ChessmanMemento save() {
return new ChessmanMemento(this.label,this.x,this.y);
}

//恢复状态
public void restore(ChessmanMemento memento) {
this.label = memento.getLabel();
this.x = memento.getX();
this.y = memento.getY();
}
}

//象棋棋子备忘录类:备忘录
class ChessmanMemento {
private String label;
private int x;
private int y;

public ChessmanMemento(String label,int x,int y) {
this.label = label;
this.x = x;
this.y = y;
}

public void setLabel(String label) {
this.label = label;
}

public void setX(int x) {
this.x = x;
}

public void setY(int y) {
this.y = y;
}

public String getLabel() {
return (this.label);
}

public int getX() {
return (this.x);
}

public int getY() {
return (this.y);
}
}

//象棋棋子备忘录管理类:负责人
import java.util.*;

class MementoCaretaker {
//定义一个集合来存储多个备忘录
private ArrayList mementolist = new ArrayList();

public ChessmanMemento getMemento(int i) {
return (ChessmanMemento)mementolist.get(i);
}

public void setMemento(ChessmanMemento memento) {
mementolist.add(memento);
}
}

class Client {
private static int index = -1; //定义一个索引来记录当前状态所在位置
private static MementoCaretaker mc = new MementoCaretaker();

public static void main(String args[]) {
Chessman chess = new Chessman("车",1,1);
play(chess);
chess.setY(4);
play(chess);
chess.setX(5);
play(chess);
undo(chess,index);
undo(chess,index);
redo(chess,index);
redo(chess,index);
}

//下棋
public static void play(Chessman chess) {
mc.setMemento(chess.save()); //保存备忘录
index ++;
System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
}

//悔棋
public static void undo(Chessman chess,int i) {
System.out.println("******悔棋******");
index --;
chess.restore(mc.getMemento(i-1)); //撤销到上一个备忘录
System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
}

//撤销悔棋
public static void redo(Chessman chess,int i) {
System.out.println("******撤销悔棋******");
index ++;
chess.restore(mc.getMemento(i+1)); //恢复到下一个备忘录
System.out.println("棋子" + chess.getLabel() + "当前位置为:" + "第" + chess.getX() + "行" + "第" + chess.getY() + "列。");
}
}


联想

和命令模式相比,命令模式也可以添加撤销功能,但备忘录模式关注的是状态保存,而命令模式关注的是请求者和执行者的解耦。

状态模式(处理对象的多种状态及其相互转换)

特性

把状态和影响状态相关的业务方法抽象出一个类,并在状态宿主环境类中添加设置状态的方法用于环境类在状态转换时调用

例子

银行账户状态根据存款多少的状态转换

//银行账户:环境类
class Account {
private AccountState state; //维持一个对抽象状态对象的引用
private String owner; //开户名
private double balance = 0; //账户余额

public Account(String owner,double init) {
this.owner = owner;
this.balance = balance;
this.state = new NormalState(this); //设置初始状态
System.out.println(this.owner + "开户,初始金额为" + init);
System.out.println("---------------------------------------------");
}

public double getBalance() {
return this.balance;
}

public void setBalance(double balance) {
this.balance = balance;
}

public void setState(AccountState state) {
this.state = state;
}

public void deposit(double amount) {
System.out.println(this.owner + "存款" + amount);
state.deposit(amount); //调用状态对象的deposit()方法
System.out.println("现在余额为"+ this.balance);
System.out.println("现在帐户状态为"+ this.state.getClass().getName());
System.out.println("---------------------------------------------");
}

public void withdraw(double amount) {
System.out.println(this.owner + "取款" + amount);
state.withdraw(amount); //调用状态对象的withdraw()方法
System.out.println("现在余额为"+ this.balance);
System.out.println("现在帐户状态为"+ this. state.getClass().getName());
System.out.println("---------------------------------------------");
}

public void computeInterest()
{
state.computeInterest(); //调用状态对象的computeInterest()方法
}
}

//抽象状态类
abstract class AccountState {
protected Account acc;
public abstract void deposit(double amount);
public abstract void withdraw(double amount);
public abstract void computeInterest();
public abstract void stateCheck();
}

//正常状态:具体状态类
class NormalState extends AccountState {
public NormalState(Account acc) {
this.acc = acc;
}

public NormalState(AccountState state) {
this.acc = state.acc;
}

public void deposit(double amount) {
acc.setBalance(acc.getBalance() + amount);
stateCheck();
}

public void withdraw(double amount) {
acc.setBalance(acc.getBalance() - amount);
stateCheck();
}

public void computeInterest()
{
System.out.println("正常状态,无须支付利息!");
}

//状态转换
public void stateCheck() {
if (acc.getBalance() > -2000 && acc.getBalance() <= 0) {
acc.setState(new OverdraftState(this));
}
else if (acc.getBalance() == -2000) {
acc.setState(new RestrictedState(this));
}
else if (acc.getBalance() < -2000) {
System.out.println("操作受限!");
}
}
}

//透支状态:具体状态类
class OverdraftState extends AccountState
{
public OverdraftState(AccountState state) {
this.acc = state.acc;
}

public void deposit(double amount) {
acc.setBalance(acc.getBalance() + amount);
stateCheck();
}

public void withdraw(double amount) {
acc.setBalance(acc.getBalance() - amount);
stateCheck();
}

public void computeInterest() {
System.out.println("计算利息!");
}

//状态转换
public void stateCheck() {
if (acc.getBalance() > 0) {
acc.setState(new NormalState(this));
}
else if (acc.getBalance() == -2000) {
acc.setState(new RestrictedState(this));
}
else if (acc.getBalance() < -2000) {
System.out.println("操作受限!");
}
}
}

//受限状态:具体状态类
class RestrictedState extends AccountState {
public RestrictedState(AccountState state) {
this.acc = state.acc;
}

public void deposit(double amount) {
acc.setBalance(acc.getBalance() + amount);
stateCheck();
}

public void withdraw(double amount) {
System.out.println("帐号受限,取款失败");
}

public void computeInterest() {
System.out.println("计算利息!");
}

//状态转换
public void stateCheck() {
if(acc.getBalance() > 0) {
acc.setState(new NormalState(this));
}
else if(acc.getBalance() > -2000) {
acc.setState(new OverdraftState(this));
}
}
}

class Client {
public static void main(String args[]) {
Account acc = new Account("段誉",0.0);
acc.deposit(1000);
acc.withdraw(2000);
acc.deposit(3000);
acc.withdraw(4000);
acc.withdraw(1000);
acc.computeInterest();
}
}


状态共享

即多个环境类共享一个状态,如点灯的开关关了,热水器、冰箱、电视机等也跟着断电

通过static来达到目的。

代码

class Switch {
private static State state,onState,offState; //定义三个静态的状态对象
private String name;

public Switch(String name) {
this.name = name;
onState = new OnState();
offState = new OffState();
this.state = onState;
}

public void setState(State state) {
this.state = state;
}

public static State getState(String type) {
if (type.equalsIgnoreCase("on")) {
return onState;
}
else {
return offState;
}
}

//打开开关
public void on() {
System.out.print(name);
state.on(this);
}

//关闭开关
public void off() {
System.out.print(name);
state.off(this);
}
}

abstract class State {
public abstract void on(Switch s);
public abstract void off(Switch s);
}

//打开状态
class OnState extends State {
public void on(Switch s) {
System.out.println("已经打开!");
}

public void off(Switch s) {
System.out.println("关闭!");
s.setState(Switch.getState("off"));
}
}

//关闭状态
class OffState extends State {
public void on(Switch s) {
System.out.println("打开!");
s.setState(Switch.getState("on"));
}

public void off(Switch s) {
System.out.println("已经关闭!");
}
}

class Client {
public static void main(String args[]) {
Switch s1,s2;
s1=new Switch("开关1");
s2=new Switch("开关2");

s1.on();
s2.on();
s1.off();
s2.off();
s2.on();
s1.on();
}
}

输出结果如下:
开关1已经打开!
开关2已经打开!
开关1关闭!
开关2已经关闭!
开关2打开!
开关1已经打开!


策略模式(处理一个功能多种实现途径的情况,android中的LayoutParams应该就是这种设计模式)

特性

如买一件商品,我们可以

原价直接购买

VIP卡购买

熟人代购

网上购买 等等…..

则我们可以将购买方式抽象出来形成一个继承链并关联相应的环境业务类,则可以做到低耦合,即使以后增加一种方式也几乎不用改动原代码。

代码(以买电影票为例子)

//电影票类:环境类
class MovieTicket {
private double price;
private Discount discount; //维持一个对抽象折扣类的引用

public void setPrice(double price) {
this.price = price;
}

//注入一个折扣类对象
public void setDiscount(Discount discount) {
this.discount = discount;
}

public double getPrice() {
//调用折扣类的折扣价计算方法
return discount.calculate(this.price);
}
}

//折扣类:抽象策略类
interface Discount {
public double calculate(double price);
}

//学生票折扣类:具体策略类
class StudentDiscount implements Discount {
public double calculate(double price) {
System.out.println("学生票:");
return price * 0.8;
}
}

//儿童票折扣类:具体策略类
class ChildrenDiscount implements Discount {
public double calculate(double price) {
System.out.println("儿童票:");
return price - 10;
}
}

//VIP会员票折扣类:具体策略类
class VIPDiscount implements Discount {
public double calculate(double price) {
System.out.println("VIP票:");
System.out.println("增加积分!");
return price * 0.5;
}
}

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
class XMLUtil {
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean() {
try {
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));

//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();

//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}

class Client {
public static void main(String args[]) {
MovieTicket mt = new MovieTicket();
double originalPrice = 60.0;
double currentPrice;

mt.setPrice(originalPrice);
System.out.println("原始价为:" + originalPrice);
System.out.println("---------------------------------");

Discount discount;
discount = (Discount)XMLUtil.getBean(); //读取配置文件并反射生成具体折扣对象
mt.setDiscount(discount); //注入折扣对象

currentPrice = mt.getPrice();
System.out.println("折后价为:" + currentPrice);
}
}


模板方法模式(设计一个过程的基本步骤框架)

特性

确定的步骤在基类方法中声明,不确定的步骤通过钩子方法确定下一步往哪走,步骤的实现都是相同的在基类中写好,步骤的实现有差异的可以抽象化给子类实现

钩子方法:?? 我个人认为可以是:

* 钩子方法是空实现或默认实现

* 子类可以修改钩子方法的实现以定制父类的行为

类结构

访问者模式(操作复杂对象结构—主要针对被处理对象类型基本不变,处理者类型多变的模式)

特性

对一组不同的对象(被访问者)要经手不同的人处理(访问者)使得

访问者提供重载的几个方法分别用于访问处理不同的被访问者,访问者应是一个继承链

被访问者应是一个抽象链,统一实现接受访问者访问的方法

这样可以使得访问者种类增加后无需修改原有代码

但被访问者种类增加后则访问者需要大改,这是一个缺点

有两个维度,一是需要判断不同的访问者区分不同的大方向的处理方式,二是需要判断不同的被访问者要区分访问者不同的具体处理方式,我们利用重载和继承链以达到解耦的目的

类结构图

代码

import java.util.*;

//员工类:抽象元素类
interface Employee
{
public void accept(Department handler); //接受一个抽象访问者访问
}

//全职员工类:具体元素类
class FulltimeEmployee implements Employee
{
private String name;
private double weeklyWage;
private int workTime;

public FulltimeEmployee(String name,double weeklyWage,int workTime)
{
this.name = name;
this.weeklyWage = weeklyWage;
this.workTime = workTime;
}

public void setName(String name)
{
this.name = name;
}

public void setWeeklyWage(double weeklyWage)
{
this.weeklyWage = weeklyWage;
}

public void setWorkTime(int workTime)
{
this.workTime = workTime;
}

public String getName()
{
return (this.name);
}

public double getWeeklyWage()
{
return (this.weeklyWage);
}

public int getWorkTime()
{
return (this.workTime);
}

public void accept(Department handler)
{
handler.visit(this); //调用访问者的访问方法
}
}

//兼职员工类:具体元素类
class ParttimeEmployee implements Employee
{
private String name;
private double hourWage;
private int workTime;

public ParttimeEmployee(String name,double hourWage,int workTime)
{
this.name = name;
this.hourWage = hourWage;
this.workTime = workTime;
}

public void setName(String name)
{
this.name = name;
}

public void setHourWage(double hourWage)
{
this.hourWage = hourWage;
}

public void setWorkTime(int workTime)
{
this.workTime = workTime;
}

public String getName()
{
return (this.name);
}

public double getHourWage()
{
return (this.hourWage);
}

public int getWorkTime()
{
return (this.workTime);
}

public void accept(Department handler)
{
handler.visit(this); //调用访问者的访问方法
}
}

//部门类:抽象访问者类
abstract class Department
{
//声明一组重载的访问方法,用于访问不同类型的具体元素
public abstract void visit(FulltimeEmployee employee);
public abstract void visit(ParttimeEmployee employee);
}

//财务部类:具体访问者类
class FADepartment extends Department
{
//实现财务部对全职员工的访问
public void visit(FulltimeEmployee employee)
{
int workTime = employee.getWorkTime();
double weekWage = employee.getWeeklyWage();
if(workTime > 40)
{
weekWage = weekWage + (workTime - 40) * 100;
}
else if(workTime < 40)
{
weekWage = weekWage - (40 - workTime) * 80;
if(weekWage < 0)
{
weekWage = 0;
}
}
System.out.println("正式员工" + employee.getName() + "实际工资为:" + weekWage + "元。");
}

//实现财务部对兼职员工的访问
public void visit(ParttimeEmployee employee)
{
int workTime = employee.getWorkTime();
double hourWage = employee.getHourWage();
System.out.println("临时工" + employee.getName() + "实际工资为:" + workTime * hourWage + "元。");
}
}

//人力资源部类:具体访问者类
class HRDepartment extends Department
{
//实现人力资源部对全职员工的访问
public void visit(FulltimeEmployee employee)
{
int workTime = employee.getWorkTime();
System.out.println("正式员工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
if(workTime > 40)
{
System.out.println("正式员工" + employee.getName() + "加班时间为:" + (workTime - 40) + "小时。");
}
else if(workTime < 40)
{
System.out.println("正式员工" + employee.getName() + "请假时间为:" + (40 - workTime) + "小时。");
}
}

//实现人力资源部对兼职员工的访问
public void visit(ParttimeEmployee employee)
{
int workTime = employee.getWorkTime();
System.out.println("临时工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
}
}

//员工列表类:对象结构
class EmployeeList
{
//定义一个集合用于存储员工对象
private ArrayList<Employee> list = new ArrayList<Employee>();

public void addEmployee(Employee employee)
{
list.add(employee);
}

//遍历访问员工集合中的每一个员工对象
public void accept(Department handler)
{
for(Object obj : list)
{
((Employee)obj).accept(handler);
}
}
}

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;
class XMLUtil
{
//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
public static Object getBean()
{
try
{
//创建文档对象
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("config.xml"));

//获取包含类名的文本节点
NodeList nl = doc.getElementsByTagName("className");
Node classNode=nl.item(0).getFirstChild();
String cName=classNode.getNodeValue();

//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}

class Client
{
public static void main(String args[])
{
EmployeeList list = new EmployeeList();
Employee fte1,fte2,fte3,pte1,pte2;

fte1 = new FulltimeEmployee("张无忌",3200.00,45);
fte2 = new FulltimeEmployee("杨过",2000.00,40);
fte3 = new FulltimeEmployee("段誉",2400.00,38);
pte1 = new ParttimeEmployee("洪七公",80.00,20);
pte2 = new ParttimeEmployee("郭靖",60.00,18);

list.addEmployee(fte1);
list.addEmployee(fte2);
list.addEmployee(fte3);
list.addEmployee(pte1);
list.addEmployee(pte2);

Department dep;
dep = (Department)XMLUtil.getBean();
list.accept(dep);
}
}


观察者模式

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式