1,原型模式
原型模式多用于当一个实例化对象创建过于复杂时,通过原型模式的clone去方法取复制一个对象,这样既可以简化实例的创建过程,也可以保存原有实例化对象的状态信息。但是原型模式并不是万能的,对于新建的类生成一个clone方法很容易,但是对于后续拓展、已有的类去生成一个clone方法,鉴于对原有类的功能实际应用上考虑,添加clone方法往往并不容易,且修改原类已经违背了设计模式的”开放-封闭”原则。先来看一下原型模式的关系UML图:
原型模式,用原型实例指定内容并且创建对象,后续通过拷贝原型对象来作为新的对象操作实例指定的内容。原型模式分浅复制和深复制。看下面这段代码来了解浅复制。
定义一个原型模型的抽象类。WorkExperience作为工作简历类实现Cloneable接口。Main方法中,通过对WorkExperience的clone()复制对象。通过输出可以发现,浅复制只复制了一个容器规格一致的对象,但是对象中的引用仍然指向原来的变量。
public class Protatype{
private String time;
private String experience;
public Protatype(){
}
public Protatype(String time,String experience){
this.time = time;
this.experience = experience;
}
@Override
public String toString() {
return "Protatype [time=" + time + ", experience=" + experience + "]";
}
public void setTime(String time) {
this.time = time;
}
public void setExperience(String experience) {
this.experience = experience;
}
}
public class WorkExperience implements Cloneable{
private String name;
private Protatype p;
public WorkExperience(String name) {
this.name =name;
}
@Override
public WorkExperience clone() throws CloneNotSupportedException {
WorkExperience w =(WorkExperience) super.clone();
return w;
}
public void getMsg(){
System.out.println(name +"+++" + p.toString());
}
public void setName(String name) {
this.name = name;
}
public void setP(Protatype p) {
this.p = p;
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
WorkExperience w = new WorkExperience("xiaoming");
Protatype p = new Protatype();
p.setExperience("1111");
p.setTime("1111");
w.setP(p);
WorkExperience w2 = w.clone();
p.setExperience("2222");
p.setTime("222222");
w2.setP(p);
w.getMsg();
w2.getMsg();
}
}
深复制,即不止对对象的复制,同时对于引用复制,可以在原型类中也实现Cloneable接口。实现clone()方法,如上代码类似。
然后在WorkExperience类中的clone方法中对于原型对象调用实现的clone()方法。
执行结果为:
2,模块方法模式
定义一个操作中的算法的骨架,将操作延迟到子类当中去。常常用来把包含数据操作,包含数据的部分代码进行protected,防止外部访问,将抽象方法的调用写入抽象父类的某个方法中,通过向上转型来调用这个入口方法。模块方法模式UML设计图如下:
下面来看一段代码。这是一个学生答题的例子。将学生答题的操作延迟到具体的子类中去操作,根据父与子类之间的向上转型来实现子类的答题操作。当然这个例子还有待完善,对于题目的后续添加与修改并没有做到上面的封闭原则。然后对于输出的辨识性也没有做到很好。该模式通过部分抽象,调用延迟在子类中的操作来减少程序的耦合性,减少代码量。就如同现在网上的答题,它不会为每一个操作者一个新建的试卷类,联想网上考试的实例相互对比。
/*题目抽象类*/
public abstract class Tim {
public abstract void Tim1();
public abstract void Tim2();
public void StudentAnswer(){
Tim1();
Tim2();
}
}
public class TimReal extends Tim{
@Override
public void Tim1() {
System.out.println("题目一的答案是:" + Tim1Answer1());
}
protected String Tim1Answer1(){
return "";
}
@Override
public void Tim2() {
System.out.println("题目二的答案是:" + Tim1Answer2());
}
protected String Tim1Answer2(){
return "";
}
}
public class Student_1 extends TimReal{
protected String Tim1Answer1(){
return "A";
}
protected String Tim1Answer2(){
return "B";
}
}
public class Student_2 extends TimReal{
protected String Tim1Answer1(){
return "C";
}
protected String Tim1Answer2(){
return "D";
}
}
public class Main {
public static void main(String[] args) {
Tim tim = new Student_1();
tim.StudentAnswer();
Tim tim2 = new Student_2();
tim2.StudentAnswer();
}
}
运行结果:
3,外观模式
客户端定义一个高层接口来给一组子接口一个统一操作的界面,使得这组子接口更加容易操控。看下面这段关于买入基金的代码,可以更好的理解这个模式。这里客户端通过购买基金,也就是所说的顶一个高层的接口,来操作股票种类这样一组接口,先说优点,客户端不用去了解股票,购入基金,由基金商家来使用来自客户的这笔钱购买股票,减少了类与类之间的耦合性。可能会认为这和代理的意思很相近,可以看一下上面的代理模式的代码,代理是将A的方法按照A的意愿去包装一层然后局部或者全部访问权限开放,A是需要了解到被代理对象的,在代理类中通过包装来连接A与C。但是外观模式,客户端完全脱离了底层一组子接口,基金类中(也是高层接口)对于这组子接口的逻辑操作代码可以多变,就如同我这里的代码里面买与卖还可以实现更多方法出来,客户端甚至不用知道高层接口具体的操作模式,只需要调用(入口)函数<买入方法>即可。
/*客户端*/
public class PersonCash {
public static void main(String[] args) {
FundCash fc = new FundCash();
fc.toBuy();
}
}
/*基金类*/
public class FundCash {
private Stock_1 s1;
private Stock_2 s2;
private Stock_3 s3;
public FundCash(){
s1 = new Stock_1();
s2 = new Stock_2();
s3 = new Stock_3();
}
public void toBuy(){
System.out.println("买入基金");
}
public void toSell(){
System.out.println("卖出基金");
}
public void buy1(){
s1.toBuy();
s2.toBuy();
}
public void buy2(){
s2.toBuy();
s3.toBuy();
}
public void sell3(){
s1.toSell();
}
public void sell4(){
s2.toSell();
s1.toSell();
}
}
/*股票一*/
public class Stock_1 {
public void toBuy(){
System.out.println("买股票一");
}
public void toSell(){
System.out.println("卖股票一");
}
}
/*股票二*/
public class Stock_2 {
public void toBuy(){
System.out.println("买股票二");
}
public void toSell(){
System.out.println("卖股票三");
}
}
/*股票三*/
public class Stock_3 {
public void toBuy(){
System.out.println("买股票三");
}
public void toSell(){
System.out.println("卖股票三");
}
}
4,建造者模式(生成器模式)
将一个复杂的对象通过构建和它的表示分离,是的同样的构建过程可以创建不同的表示内容。举个例子,我们画一个人,不考虑特殊情况,四肢,头,身体是基本构建,男女胖瘦是表示。对于人这个对象而言,基本构建是每个人都统一都有的,看成是抽象,需要被继承。男女胖瘦是不同的表示形式,看成是细节。根据依赖倒转原则,细节依赖于抽象。
看下面的代码,我这里定义了建造者模式中指挥者,指挥者的作用看客户端Main方法,它的作用将具体的组成部分完全与用户隔离,用户不用知道其中的组成部分有什么,只需要知道根据要求我需要两个人的对象即可,然后通过指挥者底层去调用然后展示给客户端即可。同样的例子还可以应用到电脑组装等等。
public abstract class Person {
public abstract void buildHead();
public abstract void buildLeftHandAndLeg();
public abstract void buildRightHandAndLeg();
public abstract void buildBody();
}
public class DrawPerson_1 extends Person{
@Override
public void buildHead() {
System.out.println("多大多大的头");
}
@Override
public void buildLeftHandAndLeg(){
System.out.println("多大多大的左手和左腿");
}
@Override
public void buildRightHandAndLeg() {
System.out.println("多大多大的右手和右脚");
}
@Override
public void buildBody() {
System.out.println("多胖多胖的身子");
}
}
public class DrawPerson_2 extends Person{
......同DrawPerson_1
}
public class Director {
List<Person> list = new ArrayList<Person>();
public void construct(Person p){
p.buildBody();
p.buildHead();
p.buildLeftHandAndLeg();
p.buildRightHandAndLeg();
list.add(p);
}
public void show(){
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
public class Main {
public static void main(String[] args) {
//要求画出来的两个人
Person p = new DrawPerson_1(); //向上转型
Person p2 = new DrawPerson_2(); //向上转型
//指挥者
Director dir = new Director();
dir.construct(p2);
dir.construct(p);
dir.show();
}
}
5,观察者模式
通过一对多,让多个观察者去监听一个主题对象。通过主题对象的状态改变,通知所有的观察者去自己改变自己的状态。下面的代码为一个秘书通知同事们老板过来了不要干别的事情了,赶紧工作的代码。这段代码虽然实现了开放封闭原则与依赖倒转原则降低了代码之间的耦合性。但是从实际角度出发,每个worker当前做的事情并不是同一件事,update的具体操作也不一样,甚至不是进行同一个动作状态的改变,这就无法使用同一个抽象接口了。这也就是观察者模式的弊端。
//主题抽象对象类
public abstract class Subject {
public abstract void addOneObserve(Observer ob);
public abstract void notifyObserveStus();
}
//观察者抽象类
public abstract class Observer {
public abstract void update();
}
//秘书类
public class OneSecretary extends Subject {
List<Observer> oblist = new ArrayList<Observer>();
@Override
public void addOneObserve(Observer ob) {
oblist.add(ob);
}
@Override
public void notifyObserveStus() {
for(int i=0;i<oblist.size();i++){
oblist.get(i).update();
}
}
}
//干别的事情的worker_1
public class WorkerObserver_1 extends Observer{
@Override
public void update() {
System.out.println("workerObserver_1停下手中的活去工作");
}
}
//干别的事情的worker_2
public class WorkerObserver_2 extends Observer{
@Override
public void update() {
System.out.println("workerObserver_2停下手中的活去工作");
}
}
//Main
public class Main {
public static void xc(String[] args) {
Subject s = new OneSecretary();
Observer wo2 = new WorkerObserver_2();
Observer wo1 = new WorkerObserver_1();
s.addOneObserve(wo1);
s.addOneObserve(wo2);
s.notifyObserveStus();
}
}
为了弥补观察模式的弊端,可以采用事件的委托来实现原理,就用eclipse的例子来说明,我们在debug模式下,点击了右边的退出debug的模式,我们会关闭当前debug的页面,然后打开Main方法执行的result界面在下面,在左边显示当前类的列表界面。显然操作不一,肯定不是通过观察者模式中一个抽象接口来实现的。现在用事件的委托来实现。因为观察者不再依赖于某一个抽象的类,所以实例对象中也就没有了del这样的方法。事件中可以搭载多个方法,事件被唤起然后唤起所有的方法,可以把委托事件当成一个引用方法类型,与搭载的该方法拥有完全相同的行为。
//事件类
public class Event {
private Class clazz;
private String methodName;
private Object[] methodParams;
public Event(Class clazz,String methodName,Object...params){
this.clazz = clazz ;
this.methodName = methodName;
this.methodParams = params;
}
public boolean invoke() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException{
for(Method method:clazz.getMethods()){
if(method.getName() == methodName){
method.invoke(clazz.newInstance(), methodParams);
return true;
}
}
return false;
}
}
public abstract class Subject {
public abstract void notifyObserveStus();
public abstract void addEvent(Class clazz,String methodName,Object...params);
}
public class SndOneSecreTary extends Subject{
private List<Event> list;
public SndOneSecreTary(){
list = new ArrayList<Event>();
}
@Override
public void addEvent(Class clazz,String methodName,Object...params){
list.add(new Event(clazz, methodName,params));
}
@Override
public void notifyObserveStus() {
try {
for(int i=0;i<list.size();i++){
list.get(i).invoke();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class WorkerObserver_1{
public void seeNBA() {
System.out.println("老板来了关掉NBA,去工作");
}
}
public class WorkerObserver_2 {
public void playGame() {
System.out.println("老板来了关掉游戏,去工作");
}
}
public class Main {
public static void main(String[] args) {
SndOneSecreTary sons = new SndOneSecreTary();
sons.addEvent(WorkerObserver_1.class, "seeNBA");
sons.addEvent(WorkerObserver_2.class, "playGame");
sons.notifyObserveStus();
}
}