Java设计模式
设计模式是在大量的实践中总结和理论化之后的优选的代码结构、编程风格、以及解决问题的思考方式。
单例设计模式
就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法。
饿汉式
在加载类的时候就会创建对象,可能会造成对象创建了但是没有使用,造成资源浪费
实现方法
- 构造方法私有化。这一步的目的是为了不让外部可以创建对象。
- 类内部创建对象,创建的对象是
static
。
- 向外暴露一个静态的公共方法,
getInstance()
。该静态的公共方法返回创建的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Animal { private String name; private static Animal animal = new Animal("大白"); private Aniaml(String name) { this.name = name; } public static Animal getInstance() { return animal; } }
public class Main { public static void main(String[] args) { Animal animal = Animal.getInstance(); } }
|
懒汉式
在使用时创建对象,只有在用户使用getInstance()
方法时才返回对象,后面再次调用时会返回上次创建的对象,从而保证了单例。
实现方法
- 构造器私有化。
- 定义一个
static
静态属性对象。
- 定义一个
public
的static
方法可以返回对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Animal { private String name; private static Animal animal; private Aniaml(String name) { this.name = name; } public static Animal getInstance() { if(animal == null) animal = new Animal("大白"); return animal; } }
public class Main { public static void main(String[] args) { Animal animal = Animal.getInstance(); } }
|
饿汉式和懒汉式的区别
- 最主要的区别是创建对象的时机不同,饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
- 饿汉式存在浪费资源的问题,懒汉式则不存在这个问题。和对象的创建时机有很大的关系
- 在
javaSE
标准类中,java.lang.Runtime
就是经典的单例模式
观察者模式
定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有依赖者都会收到通知并自动更新。
实例解释
以微信服务号为背景,介绍观察者模式:
如上图所示:服务号就是主题,使用者就是观察者,现在我们明确一下功能。
- 服务号就是主题,业务就是推送消息
- 观察者只需要订阅主题,只要有新的消息就会收到。
- 当不想此主题消息时,取消订阅。
- 只要服务号还在,就会一直有人订阅。
实例实现
1 2 3 4 5 6 7 8 9 10 11
| package com.xzt.observerpattern;
public interface Observer { void update(String msg); }
|
- 实现一个主题接口,里面声明三个函数
registerObserver
:用来添加观察者。
removeObserver
:用来删除观察者。
notifyObservers
:用来将更新的信息响应给所有的观察者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.xzt.observerpattern;
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers(); }
|
- 实现一个类,这个类实现主题接口
- 在这个类中需要定义一个List变量,用来存储所有订阅的观察者。
- 定义一个msg,用来保存需要更新的消息。
- 实现set函数,更新msg,同时调用
notifyObservers
来向所有的观察者广播更新后的消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| package com.xzt.observerpattern.impl;
import com.xzt.observerpattern.Observer; import com.xzt.observerpattern.Subject;
import java.util.ArrayList; import java.util.List;
public class ObjectFor3D implements Subject { private List<Observer> observers = new ArrayList<>();
private String msg; @Override public void registerObserver(Observer observer) { observers.add(observer); }
@Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if(index >= 0) { observers.remove(index); } }
@Override public void notifyObservers() { for (Observer observer : observers) { observer.update(msg); } }
public void setMsg(String msg) { this.msg = msg; notifyObservers(); } }
|
- 实现两观察者类。需要实现Observer接口。实现update函数。这里就输出更新的信息即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.xzt.observerpattern.impl;
import com.xzt.observerpattern.Observer; import com.xzt.observerpattern.Subject;
public class Observer1 implements Observer {
public void registerObserver(Subject subject) { subject.registerObserver(this); }
public void removeObserver(Subject subject) { subject.removeObserver(this); } @Override public void update(String msg) { System.out.println("observer1 得到 彩票号码 ---> " + msg + ", 我要记下来"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.xzt.observerpattern.impl;
import com.xzt.observerpattern.Observer; import com.xzt.observerpattern.Subject;
public class Observer2 implements Observer {
public void registerObserver(Subject subject) { subject.registerObserver(this); }
public void removeObserver(Subject subject) { subject.removeObserver(this); } @Override public void update(String msg) { System.out.println("observer2 得到 彩票号码 ---> " + msg + ", 我要告诉舍友们"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.xzt.observerpattern.test;
import com.xzt.observerpattern.Observer; import com.xzt.observerpattern.impl.ObjectFor3D; import com.xzt.observerpattern.impl.Observer1; import com.xzt.observerpattern.impl.Observer2; import com.xzt.observerpattern.innerclass.SubjectFor3d;
public class Test { public static void main(String[] args) { ObjectFor3D objectFor3D = new ObjectFor3D();
Observer1 observer1 = new Observer1(); observer1.registerObserver(objectFor3D); Observer2 observer2 = new Observer2(); observer2.registerObserver(objectFor3D);
} }
|
总结
上面的实例是自己定义了接口模拟了观察者模型的过程,在java中也提供了相应的库。java.util.Observable
和java.util.Observer
来分别表示服务者和观察者。
不一样的地方是java库中提供的是两个类,我们自己实现的是两个接口。
这个的好处体现在:当有多个主题时,每个主题的addObserver
, removeObserver
, notifyObservers
代码基本都是相同的。如果使用接口的话就没法实现代码复用,而且也没有办法使用组合的模式实现这三个方法的复用。
代理模式
Proxy Pattern
也叫做委托模式,是一个使用率非常高的模式。
定义:为其他对象提供一种代理以控制这个对象的访问。
简单理解
代理模式将原类进行封装,客户端不能直接找到原类,必须通过代理角色。即代理是原类的一个替身,客户端要找到原类,统统找代理就可以搞定。明星和经纪人就是一种代理模式。
通用类
Subject抽向主题角色:
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
RealSubject具体主题角色:
也叫被委托角色,被代理角色。他才是冤大头,是业务逻辑的具体执行者。
Proxy代理主题角色
也叫做委托类,代理类。她负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色的实现,并且在真实主题角色处理完毕后做预处理和善后处理工作。
通用源代码
1 2 3 4 5
| public interface Subject { public void request(); }
|
在接口中定义了一个request来作为方法的代表。RealSubject对它进行实现。
1 2 3 4 5 6 7
| public class RealSubject implements Subject { public void request() { } }
|
RealSubject是一个正常的业务实现类,代理模式的核心就在代理类上。
代理类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Proxy implements Subject { private Subject subject = null; public Proxy(){ this.subject = new Proxy(); } public Proxy(Object...objects ) { } public void request() { this.before(); this.subject.request(); this.after(); } private void before(){ } private void after(){ } }
|
代理模式优点
案例分析
追星族想要明星签名,必须找其经纪人搞定。
1 2 3 4 5 6 7 8 9 10 11
| package com.xzt.proxypattern.cases;
public interface IStar { public void sign(); }
|
- 定义一个歌手类,需要实现这个接口,并且实现签字函数,每个明星的签字函数不一样。明星可以有很多,例如歌手,演员,这里以歌手来实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.xzt.proxypattern.cases;
public class Singer implements IStar{ @Override public void sign() { System.out.println("明星签字:我是鸡你太美,你干嘛!!!"); } }
|
- 定义一个经纪人类,需要通过构造函数指明代理的是哪个明星。并调用该明星进行签字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.xzt.proxypattern.cases;
public class Agent implements IStar{ private IStar star;
public Agent(IStar star) { this.star = star; }
@Override public void sign() { star.sign(); } }
|
- 定义一个追星族类。首先找到歌手,再找到代理歌手的经纪人,然后找经纪人要签名。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.xzt.proxypattern.cases;
public class Idolater { public static void main(String[] args) { IStar star = new Singer(); IStar agent = new Agent(star); System.out.println("追星族:我是你的崇拜者,请签名!"); agent.sign(); } }
|
代理模式的扩展