设计模式

1.工厂设计模式

简单工厂模式

适用场景:

  • 工厂类负责创建的对象较少;
  • 客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。

优点:

  • 只需要传入一个正确的参数,就可以获取你所需要的对象,无须知道其创建的细节。

缺点:

  • 不易于扩展过于复杂的产品结构。
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
public interface ILanguages {
void out();
}

public class JavaLanguage implements ILanguages {
@Override
public void out() {
System.out.println("JavaLanguage--------------");
}
}

public class PythonLanguage implements ILanguages {
@Override
public void out() {
System.out.println("PythonLanguage--------------");
}
}

public class LanaguageFactory {
public ILanguages create(Class<?extends ILanguages> clsName){
try {
if (null != clsName){
return clsName.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}

public class Test {
public static void main(String[] args){
System.out.println("pattern----------------start");
ILanguages language = new LanaguageFactory().create(PythonLanguage.class);
language.out();
System.out.println("pattern----------------end");
}
}

工厂方法模式

工厂方法模式,Factory Method Pattern,指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。属于创建型设计模式。

适用场景:

  • 创建对象需要大量重复的代码。
  • 客户端(应用层)不依赖产品类实例如何被创建、实现等细节。
  • 一个类通过其子类来指定创建哪个对象。

优点:

  • 用户只需关心所需产品对应的工厂,无需关心创建细节。
  • 加入新产品需求符合开闭原则,提高了系统的可扩展性。

缺点:

  • 类的个数容易过多,增加了代码的复杂度。
  • 增加了系统的抽象性和理解难度。
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
public interface ILanguages {
void out();
}

public class JavaLanguage implements ILanguages {
@Override
public void out() {
System.out.println("JavaLanguage--------------");
}
}

public class PythonLanguage implements ILanguages {
@Override
public void out() {
System.out.println("PythonLanguage--------------");
}
}

public interface ILanaguageFactory {
ILanguages create();
}

public class JavaLanguageFactory implements ILanaguageFactory {
@Override
public ILanguages create() {
return new JavaLanguage();
}
}

public class PythonLanguageFactory implements ILanaguageFactory {
@Override
public ILanguages create() {
return new PythonLanguage();
}
}

public class Test {
public static void main(String[] args){
System.out.println("pattern----------------start");
ILanaguageFactory languageFactory = new JavaLanguageFactory();
ILanguages languages = languageFactory.create();
languages.out();
System.out.println("pattern----------------end");
}
}

抽象工厂模式

抽象工厂模式(Abastract Factory Pattern)是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。属于创建型设计模式。

产品族:一系列的相关的产品,整合到一起有关联性。
产品等级:同一个继承体系。

适用场景:

  • 客户端(应用层)不依赖产品类实例如何被创建、实现等细节。
  • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

优点:

  • 具体产品在应用层代码隔离,无须关心创建细节。
  • 将一个系列的产品族统一到一起创建。

缺点:

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
  • 增加了系统的抽象性和理解难度。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public interface IFile {
void setParams();
}
public interface IMethod {
void setValue();
}
public abstract class LanguageFactory {
public void init(){
System.out.println("初始化");
}

protected abstract IFile createFile();

protected abstract IMethod createMethod();
}
.......................................................
public class JavaFile implements IFile{
@Override
public void setParams() {
System.out.println("JavaFile-----setParams");
}
}
public class JavaMethod implements IMethod{
@Override
public void setValue() {
System.out.println("JavaMethod-----setValue");
}
}
public class JavaLanguageFactory extends LanguageFactory {

@Override
protected IFile createFile() {
super.init();
return new JavaFile();
}

@Override
protected IMethod createMethod() {
super.init();
return new JavaMethod();
}
.......................................................
public class PythonFile implements IFile {
@Override
public void setParams() {
System.out.println("PythonFile-----setParams");
}
}
public class PythonMethod implements IMethod {
@Override
public void setValue() {
System.out.println("PythonMethod-----setValue");
}
}
public class PythonLanguageFactory extends LanguageFactory {

@Override
protected IFile createFile() {
super.init();
return new PythonFile();
}

@Override
protected IMethod createMethod() {
super.init();
return new PythonMethod();
}
}
.......................................................
public class Test {
public static void main(String[] args){
System.out.println("pattern----------------start");
LanguageFactory factory = new JavaLanguageFactory();
factory.createFile().setParams();
factory.createMethod().setValue();
System.out.println("pattern----------------end");
}
}

比如在58同城IM source体系中,具体的业务实现,可以分为交友与同镇,同城等,三者大部分业务相同,但是也有不同的业务卡片。

2.单例模式

单例模式(Singleton Pattern) 是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点,防止重复创建。隐藏其所有的构造方法。属于创建型模式。

适用场景:

  • 确保任何情况下都绝对只有一个实例。ApplicationContext、ServletConfig、DBPool。

优点:

  • 在内存中只有一个实例,减少了内存开销。
  • 可以避免对资源的多重占用。
  • 设置全局访问点,严格控制访问。

缺点:

  • 没有接口,扩展困难。
  • 如果要扩展单例对象,只有修改代码,没有其他途径。

饿汉式单例

在单例类首次加载时就创建实例。

优点:

  • 执行效率高
  • 性能高
  • 没有任何锁

缺点:

  • 某些情况下,可能会造成内存浪费(类加载就会创建,但不一定会被使用)
    1
    2
    3
    4
    5
    6
    7
    8
    public class HungrySingleton {
    private static HungrySingleton mInstanace = new HungrySingleton();
    private HungrySingleton(){
    }
    public static HungrySingleton getInstance(){
    return mInstanace;
    }
    }

另一种写法:和上述没本质区别;

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HungryStaticSingleton {
// 类加载顺序:先静态后动态;先上,后下;先属性后方法。
private static final HungryStaticSingleton mInstance;
//在静态代码块中初始化:
static {
mInstance = new HungryStaticSingleton();
}
private HungryStaticSingleton(){
}
public static HungryStaticSingleton getInstance(){
return mInstance;
}
}

懒汉式单例

被外部类调用时才创建实例。

优点:

  • 在调用时创建实例,节省内存。

缺点:

  • 多线程时,如果同时都进入条件,会导致多线程不安全,指令重排序问题。

线程不安全

如果两个线程同时调用,有可能创建两个不同实例。

  • 同一个实例:

1.正常顺序执行,正常结果
2.同时进入条件,后一个覆盖前一个实例,异常情况。

  • 不同实例:
    同时进入条件,按顺序返回
1
2
3
4
5
6
7
8
9
10
public class LazySington {
private static LazySington mInstance;
private LazySington(){}
public static LazySington getInstance(){
if (null == mInstance){
mInstance = new LazySington();
}
return mInstance;
}
}

线程安全

增加synchronized锁:当有线程执行到条件RUNNING时,其他的线程状态是MONITOR状态,不能进入getInstance(),而是进入监听等待状态,即为阻塞。当第一线程执行完后,其他的线程状态改为了RUNNING,继续执行。
但是这个synchronized阻塞了所有资源,即使mInstance不为空,也会阻塞。

1
2
3
4
5
6
7
8
9
10
public class LazySington {
private static LazySington mInstance;
private LazySington(){}
public synchronized static LazySington getInstance(){
if (null == mInstance){
mInstance = new LazySington();
}
return mInstance;
}
}

改进,使阻塞粒度变得更小:

1
2
3
4
5
6
7
8
9
10
11
12
public class LazySington {
private static LazySington mInstance;
private LazySington(){}
public static LazySington getInstance(){
if (null == mInstance){
synchronized (LazySington.class){
mInstance = new LazySington();
}
}
return mInstance;
}
}

再次改进:看双重校验

双重校验

但是这个时候如果多线程问题,还时存在指令重排问题的:解决方案,就是增加 volatile

优点:

  • 性能高,线程安全

缺点:

  • 可读性差,多重判空
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LazySington {
private volatile static LazySington mInstance;
private LazySington(){}
public static LazySington getInstance(){
// 检查是否阻塞
if (null == mInstance){
synchronized (LazySington.class){
// 检查是否重新创建实例
if (null == mInstance){
mInstance = new LazySington();
}
}
}
return mInstance;
}
}

静态内部类

优点:

  • 写法优雅,利用了java本身的语法特点
  • 性能高,避免了内存浪费

缺点:

  • 能够被反射破坏,其实上述单例都能够被破坏。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 先加载类:首先加载LazyInnerSingleton.class
* 再加载内部类:LazyInnerSingleton&LazyInnerSingletonHolder.class
*/
public class LazyInnerSingleton {
private LazyInnerSingleton(){}
public static LazyInnerSingleton getInstance(){
return LazyInnerSingletonHolder.INSTANCE;
}
//只在使用的时候才分配内存,进行加载
private static class LazyInnerSingletonHolder{
private static final LazyInnerSingleton INSTANCE = new LazyInnerSingleton();
}
}

反射破坏单例

以静态内部类反射为例:
反射:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ReflectTest {
public static void main(String[] args){
try{
Class<?> clazz = LazyInnerSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object instance = constructor.newInstance();
System.out.println(instance);
Object instance1 = constructor.newInstance();
System.out.println(instance1);
}catch (Exception e){
e.printStackTrace();
}
}
}

输入结果:

1
2
com.javadesignpatterns.Singleton.lazy.LazyInnerSingleton@15db9742
com.javadesignpatterns.Singleton.lazy.LazyInnerSingleton@6d06d69c

所以,多个实例,违背了单例的单一原则。
所以改进,在构造方法中增加非空判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 先加载类:首先加载LazyInnerSingleton.class
* 再加载内部类:LazyInnerSingleton&LazyInnerSingletonHolder.class
*/
public class LazyInnerSingleton {
private LazyInnerSingleton(){
if (LazyInnerSingletonHolder.INSTANCE != null){
throw new RuntimeException("不允许非法的访问");
}
}
public static LazyInnerSingleton getInstance(){
return LazyInnerSingletonHolder.INSTANCE;
}
//只在使用的时候才分配内存,进行加载
private static class LazyInnerSingletonHolder{
private static final LazyInnerSingleton INSTANCE = new LazyInnerSingleton();
}
}

输出:Caused by: java.lang.RuntimeException: 不允许非法的访问

优点:

  • 写法优雅,利用了java本身的语法特点
  • 性能高,避免了内存浪费
  • 不能被反射破坏

缺点:

  • 代码不优雅。
    那如何再次改进,看注册式单例:

注册式单例

大批量创建单例,将每一个实例都缓存到统一的容器中,使用唯一标识获取实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<>();
public static Object getInstance(String className){
if (!ioc.containsKey(className)){
Object instance = null;
try {
instance = Class.forName(className).newInstance();
ioc.put(className,instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
}else {
return ioc.get(className);
}
}
}

序列化、反序列化破坏单例

1
2
3
4
5
6
7
8
9
10
11
12
public class SeriableHungrySingleton implements Serializable {
//序列化:把内存中对象的状态转换为字节码的形式,再把字节码通过IO输出流,写到磁盘上,永久保存,持久化。

//反序列化:将持久化的字节码内容通过IO输入流读到内存中来,再转换成一个对象。

private static SeriableHungrySingleton mInstanace = new SeriableHungrySingleton();
private SeriableHungrySingleton(){
}
public static SeriableHungrySingleton getInstance(){
return mInstanace;
}
}

测试:

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 SeriableHungrySingletonTest {
public static void main(String[] args){
SeriableHungrySingleton s1 = null;
SeriableHungrySingleton s2 = SeriableHungrySingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableHungrySingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();;
oos.close();

FileInputStream fis = new FileInputStream("SeriableHungrySingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableHungrySingleton)ois.readObject();
ois.close();

System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}catch (Exception e){
e.printStackTrace();
}
}
}

输出结果:两次得到的实例不一样

1
2
3
com.javadesignpatterns.seriable.SeriableHungrySingleton@776ec8df
com.javadesignpatterns.seriable.SeriableHungrySingleton@5c647e05
false

如何解决:增加一个readResolve()方法

class SeriableHungrySingleton implements Serializable {
1
2
3
4
5
    .............
private Object readResolve(){
return mInstanace;
}
}

测试,输入结果:

1
2
3
com.javadesignpatterns.seriable.SeriableHungrySingleton@5c647e05
com.javadesignpatterns.seriable.SeriableHungrySingleton@5c647e05
true

那为什么加一个readResolve,就解决了呢?需要看readObject源码,在这个源码里有一个
checkResolve(readOrdinaryObject(unshared));

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
private Object readOrdinaryObject(boolean unshared)
。。。。。。。。。。。。。。。。。。。。。
Object obj;
try {
// newInstance,创建实例
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}

passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}

if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}

handles.finish(passHandle);
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
//通过反射获取ReadResolve对象,这个就是我们单例中readResolve返回的对象
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
//如果跟newInstance的对象不一样,就赋值给obj,返回,这个就不时新创建新的对象了。
if (rep != obj) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}

return obj;
}

private Object checkResolve(Object obj) throws IOException {
if (!enableResolve || handles.lookupException(passHandle) != null) {
return obj;
}
Object rep = resolveObject(obj);
if (rep != obj) {
// The type of the original object has been filtered but resolveObject
// may have replaced it; filter the replacement's type
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, rep);
}
return rep;
}

ThreadLocal单例

保证线程内部的全局唯一,且天生线程安全。但是在不同的线程,肯定是不同的。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ThreadLocalSingleton {
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
private ThreadLocalSingleton(){}
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
}

测试:

1
2
3
4
5
6
7
8
public class ThreadLocalSingletonTest {
//在主线程
public static void main(String[] args){
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
}
}

输出:

1
2
3
com.javadesignpatterns.Singleton.threadlocal.ThreadLocalSingleton@15db9742
com.javadesignpatterns.Singleton.threadlocal.ThreadLocalSingleton@15db9742
com.javadesignpatterns.Singleton.threadlocal.ThreadLocalSingleton@15db9742

ThreadLocal:getEntry(this),就是当前线程位key。从map中获取:

1
2
3
4
5
6
7
8
9
10
11
12
13
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

总结

  • 1.私有化构造方法。
  • 2.保证线程安全。
  • 3.延迟加载。
  • 4.防止序列化和反序列化破坏单例。
  • 5.防止反射攻击单例。

3.原型模式

原型模式(Prototype Pattern),是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数。属于创建型模式。

适用场景:

  • 类初始化消耗资源较多。
  • new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)。
  • 构造函数比较复杂。
  • 循环体中生成大量对象时。

优点:

  • 在内存中只有一个实例,减少了内存开销,性能优良。
  • 可以避免对资源的多重占用。
  • 设置全局访问点,严格控制访问。
  • java自带的原型模式是基于内存二进制流的拷贝,比直接new对象性能上提升很多。
  • 可以用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了流程。

缺点:

  • 必须配备克隆方法。
  • 当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
  • 深拷贝、浅拷贝需要运用得当。
  • 原型模式和单例模式的使用是互斥的。

通用写法举例:

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
public interface IPrototype<T> {
T clone();
}

public class LanguageBean implements IPrototype{
public String id;
public String name;
public String nickName;

@Override
public LanguageBean clone() {
LanguageBean languageBean = new LanguageBean();
languageBean.id = "2";
languageBean.name ="java";
languageBean.nickName="javaName";
return languageBean;
}

@Override
public String toString() {
return "LanguageBean{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
'}';
}
}
//也可以通过反射赋值clone,在58时曾经用过这种方式。
public class LanguageUtils {
public static Object copy(Object prototype){
Class clazz = prototype.getClass();
Object objectVlue = null;
try{
objectVlue = clazz.newInstance();
for (Field field: clazz.getDeclaredFields()){
field.setAccessible(true);
field.set(objectVlue,field.get(prototype));
}
}catch (Exception e){
e.printStackTrace();
}
return objectVlue;
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
public static void main(String[] args){
System.out.println("pattern----------------start");
//创建原型对象
LanguageBean languageBean = new LanguageBean();
languageBean.id = "2";
languageBean.name ="java";
languageBean.nickName="javaName";
System.out.println(languageBean);
// 拷贝原型对象
LanguageBean cloneBean = languageBean.clone();
System.out.println(cloneBean);
System.out.println("pattern----------------end");
}
}

输出:一模一样,通过方法clone,没有通过new。

1
2
3
4
pattern----------------start
LanguageBean{id='2', name='java', nickName='javaName'}
LanguageBean{id='2', name='java', nickName='javaName'}
pattern----------------end

浅克隆

使用jdk的Cloneable接口,它的实现是在Object类中,而Object的clone是一个native方法。

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
/**
* @author unascribed
* @see java.lang.CloneNotSupportedException
* @see java.lang.Object#clone()
* @since JDK1.0
*/
public interface Cloneable {
}

public class Object {
protected native Object clone() throws CloneNotSupportedException;
}

public class LanguageBean implements Cloneable {
public String id;
public String name;
public String nickName;
public List<String> method;//添加一个list

@Override
public LanguageBean clone() {
try{
return (LanguageBean)super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}

}

@Override
public String toString() {
return "LanguageBean{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
", method=" + method +
'}';
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args){
System.out.println("pattern----------------start");
//创建原型对象
LanguageBean languageBean = new LanguageBean();
languageBean.id = "2";
languageBean.name ="java";
languageBean.nickName="javaName";
List<String> listMethod = new ArrayList<String>();
listMethod.add("money");
listMethod.add("cat");
languageBean.method = listMethod;
System.out.println(languageBean);
// 拷贝原型对象
LanguageBean cloneBean = languageBean.clone();
cloneBean.method.add("dog");
System.out.println(cloneBean);
System.out.println(languageBean);
System.out.println(languageBean==cloneBean);
System.out.println("pattern----------------end");
}

输入:

1
2
3
4
5
6
pattern----------------start
LanguageBean{id='2', name='java', nickName='javaName', method=[money, cat]}
LanguageBean{id='2', name='java', nickName='javaName', method=[money, cat, dog]}
LanguageBean{id='2', name='java', nickName='javaName', method=[money, cat, dog]}
false
pattern----------------end

可以看到,第2和3打印的结果竟然一致,但是我们第三个原型对象并没有添加dog。并且原型和克隆对象的地址不是一个(打印的结果时false)。
再打印下List对象:

1
2
3
System.out.println(cloneBean.method);
System.out.println(languageBean.method);
System.out.println(languageBean.method==cloneBean.method);

输入:

1
2
3
[money, cat, dog]
[money, cat, dog]
true

所以,两个对象中的List地址是一样的,也就是共用了一个List对象。所以,通过Cloneable的clone的对象,如果是引用类型属性,只是拷贝了内存地址,并没有拷贝具体的元素。所以当改变元素内容时,也改变了原型对象内的元素。
解决方案:深克隆

深克隆

不使用默认的clone,而是通过序列化和反序列化进行克隆,或者通过转换成JSON:

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
47
48
49
50
51
52
53
54
public class LanguageBean implements Cloneable , Serializable {
public String id;
public String name;
public String nickName;
public List<String> method;

@Override
public LanguageBean clone() {
try{
return (LanguageBean)super.clone();
}catch (Exception e){
e.printStackTrace();
return null;
}
}

// 采用ArrayList的深克隆方式
public LanguageBean deepCloneMethod() {
try{
LanguageBean languageBean = (LanguageBean)super.clone();
languageBean.method = (List<String>) ((ArrayList)languageBean.method).clone();
return languageBean;
}catch (Exception e){
e.printStackTrace();
return null;
}
}

//通过序列化克隆
public LanguageBean deepClone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (LanguageBean)ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

@Override
public String toString() {
return "LanguageBean{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", nickName='" + nickName + '\'' +
", method=" + method +
'}';
}
}

测试一下啊:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args){
System.out.println("pattern----------------start");
//创建原型对象
LanguageBean languageBean = new LanguageBean();
languageBean.id = "2";
languageBean.name ="java";
languageBean.nickName="javaName";
List<String> listMethod = new ArrayList<String>();
listMethod.add("money");
listMethod.add("cat");
languageBean.method = listMethod;
System.out.println(languageBean);
// 拷贝原型对象
LanguageBean cloneBean = languageBean.deepClone();
cloneBean.method.add("dog");
System.out.println(cloneBean);
System.out.println(languageBean);
System.out.println(languageBean==cloneBean);

System.out.println(cloneBean.method);
System.out.println(languageBean.method);
System.out.println(languageBean.method==cloneBean.method);
System.out.println("pattern----------------end");
}

输出结果:没问题了

1
2
3
4
5
6
7
8
9
pattern----------------start
LanguageBean{id='2', name='java', nickName='javaName', method=[money, cat]}
LanguageBean{id='2', name='java', nickName='javaName', method=[money, cat, dog]}
LanguageBean{id='2', name='java', nickName='javaName', method=[money, cat]}
false
[money, cat, dog]
[money, cat]
false
pattern----------------end

但是这个deepClone,也有可能是有问题的,如果这个原型是个单例,就有可能被破坏。所以如果是单例,就不能实现Cloneable的clone接口,或者实现的话,就直接返回instance。所以原型和单例其实是互斥的。

4.适配器模式

适配器模式(Adapter Pattern)也叫变压器模式,它的功能是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而导致无法在一起工作的两个类能够一起工作。属于结构性设计模式。

适用场景:

  • 已经存在的类,它的方法和需求不匹配的情况。
  • 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

优点:

  • 解决兼容问题。
  • 能提高类的透明性和复用,现有的类复用但不需要改变。
  • 目标类和适配器类解耦,提高程序的扩展性。
  • 在很多业务场景中符合开闭原则。

缺点:

  • 适配器在编写过程中需要全名考虑,可能会增加系统的复杂性。
  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变的凌乱。

类适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public interface Its {
String outOther();
}
//业务角色
public class JavaBean {
public String out(){
String nm = "java bean";
System.out.println(nm);
return nm;
}
}
//转换成另外一种角色
public class lAdapter extends JavaBean implements Its{
@Override
public String outOther() {
String javabean = out();
String pythonBean = javabean + " to python bean";
System.out.println(pythonBean);
return pythonBean;
}
}

对象适配器

跟类适配器的区别就是在适配器中,不是通过继承,而是通过对象的传递。其实我们在Android中的adapter采用的就是这种。通过不同type,传递不同的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class lAdapter implements Its {
private JavaBean javaBean;
public lAdapter(JavaBean javaBean){
this.javaBean = javaBean;
}
@Override
public String outOther() {
String javabean = javaBean.out();
String pythonBean = javabean + " to python bean";
System.out.println(pythonBean);
return pythonBean;
}
}

接口适配器

所有的业务操作都定义在目标Target中,具体的逻辑处理在适配器里:

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
public interface Target {
int request1();
int request2();
}

public abstract class Adapter implements Target {
protected Adaptee adaptee;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}

public int request1(){
return 0;
}
public int request2(){
return 0;
}
}

public class Adaptee {
public int specificRequest(){
return 220;
}
}

public class Test {
public static void main(String[] args){
System.out.println("pattern----------------start");
Target adapter = new Adapter(new Adaptee());
adapter.request1();
adapter.request2();
System.out.println("pattern----------------end");
}
}

5.建造者模式

建造者模式(Builder Pattern)是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

特征:

  • 用户只需要指定需要建造的类型就可以获得对象,建造过程及细节不需要了解。
    属于创建型模式。

适用场景:

  • 适用于创建对象需要很多步骤,但是步骤的顺序不一定固定。
  • 如果一个对象有非常复杂的内部结构,属性比较多。
  • 把复杂对象的创建和使用分离。

优点:

  • 封装性好,创建和使用分离。
  • 扩展性好,建造类之间独立,一定程度上解耦。
  • 使用方便。

缺点:

  • 产生多余的Builder对象。
  • 产品内部发生变化,建造者都要修改,成本较大。
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
public class Course {
public String name;
public String pppt;
public String video;
public String note;
public String work;
}

public class CourseBuilder implements IBuilder{
private Course course = new Course();
public CourseBuilder addName(String name){
course.name = name;
return this;
}
public CourseBuilder addPpt(String ppt){
course.pppt = ppt;
return this;
}
public CourseBuilder addVideo(String vido){
course.video = vido;
return this;
}
public CourseBuilder addNote(String note){
course.note = note;
return this;
}
public CourseBuilder addWork(String work){
course.work = work;
return this;
}

@Override
public Course builder() {
return course;
}
}

public static void main(String[] args){
CourseBuilder builder = new CourseBuilder();
builder.addName("建造者")
.addNote("备注")
.addPpt("PPT")
.addPpt("PPT")
.addVideo("视频");
System.out.print(builder.builder());
}

6.建造者模式和工厂模式的区别

  • 1.建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象。
  • 2.创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的都一样。
  • 3.关注点不同:工厂模式只需要把对象创建出来就可以了,而建造者模式中不仅要创建出这个对象,还要知道这个对象由哪些部件组成。
  • 4.建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。

7.代理模式

代理模式(Proxy Pattern),指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。属于结构性设计模式。

适用场景:

  • 保护目标对象。
  • 增强目标对象。

优点:

  • 代理模式能将代理对象与真实被调用的目标对象分离。
  • 一定程度上降低了系统的耦合程度,易于扩展。
  • 代理可以起到保护目标对象的作用。
  • 增强目标对象的职责。

缺点:

  • 代理模式会造成系统设计中类的数目增加。
  • 再客户端和目标对象之间增加了一个代理对象,七请求处理速度变慢。
  • 增加了系统的复杂度。

静态代理

显示生命被代理对象。

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
public interface IPerson {
void findLove();
}

public class Person implements IPerson {
@Override
public void findLove() {
System.out.println("漂亮");
}
}

public class PersonProxy implements IPerson{
private Person person;
public PersonProxy(Person person){
this.person = person;
}
@Override
public void findLove() {
System.out.println("找人");
person.findLove();
System.out.println("成功");

}
}

public static void main(String[] args){
PersonProxy personProxy = new PersonProxy(new Person());
personProxy.findLove();
}

问题:只能代理一个Person。那如果代理多个呢?使用动态代理

动态代理

动态配置和替换被代理对象。

JDK实现

具体实现已经有了类库: java.lang.reflect.Proxy

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public interface IPerson {
void findLove();
void buy();
}

public class Person1 implements IPerson {
@Override
public void findLove() {
System.out.println("漂亮");
}

@Override
public void buy() {
System.out.println("要买车子");
}
}


public class Person2 implements IPerson {
@Override
public void findLove() {
System.out.println("学历高");
}

@Override
public void buy() {
System.out.println("要买房子");
}
}


public class JdkProxy implements InvocationHandler{
private IPerson target;
public IPerson getInstance(IPerson target) {
this.target = target;
Class<?> clazz = target.getClass();
/* return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

return null;
}
});*/
return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target,args);
after();
return result;
}

public void before(){
System.out.println("before proxy is called");
}
public void after(){
System.out.println("after proxy is called");
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args){
JdkProxy personProxy = new JdkProxy();
IPerson person1 = personProxy.getInstance(new Person1());
person1.findLove();

IPerson person2 = personProxy.getInstance(new Person2());
person2.findLove();
person2.buy();
}
}

输入结果:

1
2
3
4
5
6
7
8
9
before proxy is called
漂亮
after proxy is called
before proxy is called
学历高
after proxy is called
before proxy is called
要买房子
after proxy is called

只需要一个JdkProxy类,就可以代理Person1,和Person2的代理.

cglib 实现

因为我的演示时Android工程,但是cglib在Android不能使用,这里就不实现了。

原理

我们断点看一下,实际上jdk Proxy给我们动态生成了一个实现类:这个类以$符号开头,在内存中可见,在外面看不到。Proxy0,是生成第0个代理类。

那具体这个类是什么样?我们把这个类在拿出来,反编译生成类,存起来。
我们知道类名$Proxy0,然后通过ProxyGenerator生成class文件。

1
2
3
4
5
6
7
8
try {
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{IPerson.class});
FileOutputStream os = new FileOutputStream("E:\\JavaDesignPatterns\\src\\main\\java\\com\\javadesignpatterns\\proxy\\dynamicproxy\\jdk\\$Proxy0.class");
os.write(bytes);
os.close();
} catch (Exception e) {
e.printStackTrace();
}

class文件读取不容易,我们可以再通过jad反编译成jad文件:这个jax文件其实就是java文件内容。
E:\JavaDesignPatterns\src\main\java\com\javadesignpatterns\proxy\dynamicproxy\jdk>jad $Proxy0.class

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)

import com.javadesignpatterns.proxy.dynamicproxy.jdk.IPerson;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
implements IPerson
{

public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}

public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}

//实现了接口IPerson方法
public final void findLove()
{
try
{
// 这个h是什么?应该是父类Proxy中一个对象。
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}

public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}

public final void buy()
{
try
{
super.h.invoke(this, m4, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}

public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}

private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;

static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("com.javadesignpatterns.proxy.dynamicproxy.jdk.IPerson").getMethod("findLove", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.javadesignpatterns.proxy.dynamicproxy.jdk.IPerson").getMethod("buy", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}

我们再看看Proxy类中h是什么:我们调用的是

1
Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);

所以h就是this,也就是我们的代理类JdkProxy:

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
47
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//这里会带哦用Proxy的构造方法,并把h赋值给InvocationHandler
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
}
}

protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}

所以,我们的jax文件中 super.h.invoke(this, m3, null);其实就是调用的JdkProxy中的invoke方法。那参数:

  • this:就是生成的对象$Proxy0

  • m3:就是IPerson的findLove的方法

    1
    m3 = Class.forName("com.javadesignpatterns.proxy.dynamicproxy.jdk.IPerson").getMethod("findLove", new Class[0]);
  • null:没有参数

手写动态代理

其实根据java.lang.reflect.Proxy原理,我们可以自己实现一个动态代理。手动生成$Proxy0.java类。有空再写吧。

8.门面模式 & 外观模式

门面模式(Facade Pattern),提供一个统一的接口,用来访问子系统中的一群接口。

特征:
门面模式定义了一个高级接口,让子系统更容易使用。
属于结构型模式。

适用场景:

  • 子系统越来越复杂,增加门面模式提供简单接口。
  • 构建多层系统结构,利用门面对象作为每层的入口,简化层间调用。

优点:

  • 简化了调用过程,无需深入了解子系统,以防给子系统带来风险。
  • 减少系统依赖、松散耦合。
  • 更好地划分访问层次,提高了安全性。
  • 遵顼迪米特法则,即最少知道原则。

缺点:

  • 当增加子系统和扩展子系统行为时,可能容易带来未知风险。
  • 不符合开闭原则。
  • 某些情况下可能违背单一职责原则。
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
47
48
49
public class GiftInfo {
public String name;
public GiftInfo(String name){
this.name = name;
}
}

public class QualifyService {
public boolean qualify(GiftInfo giftInfo){
System.out.println(giftInfo.name + "10积分");
return true;
}
}

public class PayService {
public boolean pay(GiftInfo giftInfo){
System.out.println("支付"+giftInfo.name + "积分成功");
return true;
}
}

public class ShippingService {
public String delivery(GiftInfo giftInfo){
System.out.println(giftInfo.name + "快递中");
return "666";
}
}

//门面
public class FacadeService {
private QualifyService qualifyService = new QualifyService();
private PayService payService = new PayService();
private ShippingService shippingService = new ShippingService();

public void exchange(GiftInfo giftInfo){
if (qualifyService.qualify(giftInfo)){
if (payService.pay(giftInfo)){
System.out.println(shippingService.delivery(giftInfo));
}
}
}
}

public static void main(String[] args){
System.out.println("pattern----------------start");
FacadeService facadeService = new FacadeService();
facadeService.exchange(new GiftInfo("礼物信息"));
System.out.println("pattern----------------end");
}

9.装饰器模式

装饰器模式(Decorator Pattern)也叫包装模式(Wrapper Pattern),是指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)。属于结构性模式。

适用场景:

  • 用于扩展一个类的功能或给一个类添加附加职责。
  • 动态的给一个对象添加功能,这些功能可以再动态的撤销。

优点:

  • 装饰器是继承的有力补充,比继承灵活,不改变原有对象的情况下动态的给一个对象扩展功能,即插即用。
  • 通过使用不同装饰类以及这些装饰类的排列组合,可实现不同效果。
  • 装饰器完全遵守开闭原则。

缺点:

  • 会出现更多的代码和类,增加了程序的复杂性。
  • 动态装饰时,多层装饰时会更加复杂。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public abstract class Battercake {
protected abstract String getName();
public abstract int getPrice();
}

//抽象装饰器
public class BatterDecortor extends Battercake {
private Battercake battercake;
public BatterDecortor(Battercake battercake){
this.battercake = battercake;
}
@Override
protected String getName() {
return this.battercake.getName();
}

@Override
public int getPrice() {
return this.battercake.getPrice();
}
}

public class BaseBatterCake extends Battercake{
@Override
protected String getName() {
return "煎饼";
}

@Override
public int getPrice() {
return 10;
}
}

public class EggDecortor extends BatterDecortor {
public EggDecortor(Battercake battercake){
super(battercake);
}
@Override
protected String getName() {
return super.getName()+"鸡蛋";
}

@Override
public int getPrice() {
return super.getPrice() +2;
}
}

public class SauageDecortor extends BatterDecortor {
public SauageDecortor(Battercake battercake){
super(battercake);
}
@Override
protected String getName() {
return super.getName()+"香肠";
}

@Override
public int getPrice() {
return super.getPrice() +4;
}
}

测试:

1
2
3
4
5
6
7
8
9
public static void main(String[] args){
Battercake battercake = new BaseBatterCake();
System.out.println(battercake.getName() +":"+ battercake.getPrice()+ "元");
battercake = new EggDecortor(battercake);
System.out.println(battercake.getName() +":"+ battercake.getPrice()+ "元");
battercake = new SauageDecortor(battercake);
System.out.println(battercake.getName() +":"+ battercake.getPrice()+ "元");

}

输出:

1
2
3
煎饼:10元
煎饼鸡蛋:12元
煎饼鸡蛋香肠:16元

10.装饰器模式和代理模式对比

    1. 装饰器模式就是一种特殊的代理模式。
    1. 装饰器模式强调自身的功能扩展,用自己说了算的透明扩展,可动态定制的扩展。
    1. 代理模式强调代理过程的控制。

11.享元模式

享元模式(Flyweight Pattern)又称为轻量级模式,是对象池的一种实现。类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。提供了减少对象数量从而改善应用所需的对象结构的方式。

宗旨:

  • 共享细粒度对象,将多个对统一对象的访问集中起来。
    属于结构性模式。

适用场景:

  • 常常用于系统底层的开发,以便解决系统的性能问题。
  • 系统有大量相似的对象、需要缓冲池的场景。

优点:

  • 减少对象的创建,降低内存中对象的数量,降低系统的内存使用,提高效率。
  • 减少内存之外的其他资源占用。

缺点:

  • 关注内外部状态,关注线程安全问题。
  • 使系统、内存的逻辑复杂化。

不举例了,Android中Message就是一种典型的享元模式。

12.组合模式

组合模式(Composite Pattern),也成为整体-部分(Part-Whole)模式,它的宗旨是通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示。作用就是使客户端对单个对象和组合对象保持一致的方式处理。属于结构性模式。

适用场景:

  • 希望客户端可以忽略组合对象与单个对象的差异时;
  • 对象层次具备整体和部分,呈树形结构(如树形菜单、操作系统目录结构、公司组织架构等)。

优点:

  • 清楚的定义分层次的复杂对象,表示对象的全部或者部分层次。
  • 让客户端忽略了层次的差异,方便对整个层次结构进行控制。
  • 简化客户端代码。
  • 符合开闭原则。

缺点:

  • 限制类型时会较为复杂。
  • 使设计变得更加抽象。

典型的就是HashMap.putAll,put方法就是putVal,而putAll是put一个map,而map最终还是通过put Entry循环putVal完成数据存储。

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 void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}

final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold)
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
//最终都是put一个key,value
putVal(hash(key), key, value, false, evict);

透明组合模式

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public abstract class CourseComponet {
public void addChild(CourseComponet componet){
throw new UnsupportedOperationException("不支持添加");
}
public void removeChild(CourseComponet componet){
throw new UnsupportedOperationException("不支持删除");
}
public String getName(CourseComponet componet){
throw new UnsupportedOperationException("不支持获取名称");
}
public double getPrice(CourseComponet componet){
throw new UnsupportedOperationException("不支持获取价格");
}
public void print(){
throw new UnsupportedOperationException("不支持打印");
}
}

public class Course extends CourseComponet {
private String name;
private double price;
public Course(String name, double price){
this.name = name;
this.price = price;
}

@Override
public String getName(CourseComponet componet) {
return this.name;
}

@Override
public double getPrice(CourseComponet componet) {
return this.price;
}

@Override
public void print() {
System.out.println(this.name+":"+this.price+"元");
}
}

public class CoursePackage extends CourseComponet{
private List<CourseComponet> items = new ArrayList<>();
private String name;
private Integer level;
public CoursePackage(String name, int level){
this.name = name;
this.level = level;
}

@Override
public void addChild(CourseComponet componet) {
items.add(componet);
}

@Override
public void removeChild(CourseComponet componet) {
items.remove(componet);
}

@Override
public String getName(CourseComponet componet) {
return this.name;
}

@Override
public void print() {
System.out.println(this.name);
for (CourseComponet courseComponet :items){
if (this.level != null){
for (int i=0;i<this.level;i++){
System.out.print(" ");
}

for (int i=0;i<this.level;i++){
if (i == 0){
System.out.print("+");
}
System.out.print("-");
}
}
courseComponet.print();
}

}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args){
System.out.println("===================透明组合模式==============");
CourseComponet java = new Course("Java",2000);
CourseComponet ai = new Course("ai",8000);
CourseComponet coursePackage = new CoursePackage("架构师",2);

CourseComponet python = new Course("python",6000);
CourseComponet css = new Course("css",3000);

coursePackage.addChild(python);
coursePackage.addChild(css);

CourseComponet catalog = new CoursePackage("课程目录",1);
catalog.addChild(java);
catalog.addChild(ai);
catalog.addChild(coursePackage);

catalog.print();
}

输出结果:根据level输出等级的结果

1
2
3
4
5
6
7
===================透明组合模式==============
课程目录
+-Java:2000.0元
+-ai:8000.0元
+-架构师
+--python:6000.0元
+--css:3000.0元

安全组合模式

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public abstract class Direcotry {
protected String name;
public Direcotry(String name){
this.name = name;
}
public abstract void show();
}

public class File extends Direcotry{
public File(String name){
super(name);
}
@Override
public void show() {
System.out.println(this.name);
}
}

public class Folder extends Direcotry{
private List<Direcotry> dirs;
private Integer level;
public Folder(String name,Integer level){
super(name);
this.level = level;
this.dirs = new ArrayList<>();
}
@Override
public void show() {
System.out.println(this.name);
for (Direcotry direcotry :dirs){
if (this.level != null){
for (int i=0;i<this.level;i++){
System.out.print(" ");
}

for (int i=0;i<this.level;i++){
if (i == 0){
System.out.print("+");
}
System.out.print("-");
}
}
direcotry.show();
}
}

public boolean add(Direcotry direcotry){
return this.dirs.add(direcotry);
}

public boolean remove(Direcotry direcotry){
return this.dirs.remove(direcotry);
}

public Direcotry get(int index){
return this.dirs.get(index);
}

public void list(){
for (Direcotry direcotry:this.dirs){
System.out.println(direcotry.name);

}
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args){
System.out.println("===================安全组合模式==============");
File java = new File("Java");
File python = new File("Python");

Folder office = new Folder("办公",2);

File word = new File("Word");
File ppt = new File("ppt");
office.add(word);
office.add(ppt);

Folder root = new Folder("D:",1);
root.add(java);
root.add(python);
root.add(office);
System.out.println("===================show==============");

root.show();
System.out.println("===================list==============");
root.list();
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
===================安全组合模式==============
===================show==============
D:
+-Java
+-Python
+-办公
+--Word
+--ppt
===================list==============
Java
Python
办公

13.桥接模式

桥接模式(Bridge Pattern)也称为桥梁模式、接口(Interface)模式或柄体(Handle and Body)模式,是将抽象部分与它的具体实现部分分离,使它们都可以独立的变化。
通过组合的方式建立两个类之间的联系,而不是继承。
属于结构性模式。

适用场景:

  • 在抽象和具体实现之间需要增加更多的灵活性的场景。
  • 一个类存在两个(或多个)独立变化的维度,而这两个(或多个)维度都需要独立进行扩展。
  • 不希望使用继承,或因为多层继承导致系统类的个数剧增。

优点:

  • 分离抽象部分以及其具体实现部分。
  • 提高了系统的扩展性。
  • 符合开闭原则。
  • 符合合成复用原则。

缺点:

  • 增加了系统的理解与设计难度。
  • 需要正确的识别系统中两个独立变化的维度。

两种消息Email和SMS,紧急程度:Normal普通和Urgency加急,桥接类AbastractMessage,当需要给消息加急时,把消息对象给了桥接类,桥接类有两个子类普通和加急。所以,桥接模式,就是通过桥接类,对两个不同维度进行封装。

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
47
public interface IMessage {
void sendMessage(String msg,String toUser);
}
public class EmailMessage implements IMessage{
@Override
public void sendMessage(String msg, String toUser) {
System.out.println("邮件发送内容:"+msg+" 给谁"+toUser);
}
}
public class SmsMessage implements IMessage{
@Override
public void sendMessage(String msg, String toUser) {
System.out.println("SMS发送内容:"+msg+" 给谁"+toUser);
}
}

public class NormalMessage extends AbastractMessage{
public NormalMessage(IMessage message) {
super(message);
}
}
//紧急消息
public class UrgencyMessage extends AbastractMessage{
public UrgencyMessage(IMessage message) {
super(message);
}

public void sendMessage(String msg, String toUser) {
msg = "加急 : "+msg;
super.sendMessage(msg,toUser);
}

public Object watch(String id){
return null;
}
}

public abstract class AbastractMessage {
private IMessage message;
public AbastractMessage(IMessage message){
this.message = message;
}

public void sendMessage(String msg, String toUser) {
this.message.sendMessage(msg,toUser);
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args){

IMessage message = new SmsMessage();
AbastractMessage abastractMessage = new NormalMessage(message);
abastractMessage.sendMessage("加班申请","王老板");
System.out.println("===================普通==============");

message = new EmailMessage();
abastractMessage = new UrgencyMessage(message);
abastractMessage.sendMessage("加班申请","王老板");
System.out.println("===================加急==============");

}

输出:

1
2
3
4
SMS发送内容:加班申请 给谁王老板
===================普通==============
邮件发送内容:加急 : 加班申请 给谁王老板
===================加急==============

14.委派模式

委派模式(Delegate Pattern)又叫委托模式。它的基本作用是负责任务的调度和任务分配,将任务的分配和执行分离开来。可以看作是一种特殊情况下的静态代理的全权代理。
不属于GOF 23种设计模式之一。
属于行为型模式。

适用场景:

  • 委派对象本身不知道如何处理一个任务(或一个请求),把请求交给其他对象来处理。
  • 实现程序的解耦。

优点:

  • 分离抽象部分以及其具体实现部分。
  • 提高了系统的扩展性。
  • 符合开闭原则。
  • 符合合成复用原则。
  • 通过任务委派能够将一个大型的任务细化,然后通过统一管理这些子任务的完成情况实现任务的跟进,能够加快任务执行的效率。

缺点:

  • 增加了系统的理解与设计难度。
  • 需要正确的识别系统中两个独立变化的维度。
  • 任务委派方式需要根据任务的复杂程度进行不同的改变,再任务比较复杂的情况下可能需要进行多重委派,容易造成紊乱。
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
47
48
49
//老板发任务
public class Boos {
public void command(String task,Leader leader){
leader.doing(task);
}
}
//代理着经理分配任务,啥事不干
public class Leader implements IEmployee {
private Map<String,IEmployee> employees = new HashMap<>();
public Leader(){
employees.put("Banner",new EmplyeeA());
employees.put("List",new EmplyeeB());
}
@Override
public void doing(String task) {
/*if ("Banner".equals(task)){
new EmplyeeA().doing(task);
} else if ("List".equals(task)){
new EmplyeeB().doing(task);
}else{
System.out.println("这个任务"+task+"做不了,人手不够");
}*/

if (!employees.containsKey(task)){
System.out.println("这个任务"+task+"做不了,人手不够");
return;
}
employees.get(task).doing(task);
}
}

//员工
public interface IEmployee {
void doing(String task);
}
public class EmplyeeA implements IEmployee {
protected String goodAt = "Banner";
@Override
public void doing(String task) {
System.out.println("我是员工A,擅长 "+ goodAt +",接受工作"+task);
}
}
public class EmplyeeB implements IEmployee {
protected String goodAt = "List";
@Override
public void doing(String task) {
System.out.println("我是员工B,擅长 "+ goodAt +",接受工作"+task);
}
}

测试:

1
2
3
4
5
6
7
public class Test {
public static void main(String[] args){
new Boos().command("Banner",new Leader());
new Boos().command("List",new Leader());
new Boos().command("Ad",new Leader());
}
}

输出:

1
2
3
我是员工A,擅长 Banner,接受工作Banner
我是员工B,擅长 List,接受工作List
这个任务Ad做不了,人手不够

还有ClassLoader源码也是典型的双亲委派模式,避免重复加载。

15.委派模式和代理模式的区别

  • 委派模式是行为型模式,代理模式是结构型模式。
  • 委派模式注重的是任务派遣,注重结果;代理模式注重的是代码增强,注重过程。
  • 委派模式是一种特殊的静态代理,相当于全权代理。

16.模板方法模式

模板方法模式(Template Method Pattern)通常又叫模板模式,是指定一个算法的骨架,并允许子类为其中的一个或者多个步骤提供实现。
模板方法使得子类可以再不改变算法结构的情况下,重新定义算法的某些步骤。
属于行为型设计模式。

适用场景:

  • 一次性实现一个算法不变的部分,并将可变的行为留给子类来实现。
  • 各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。

优点:

  • 利用模板方法将相同处理逻辑的代码放到抽象父类中,可以提高代码的复用性。
  • 将不同的代码放在不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性。
  • 把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则。

缺点:

  • 类数目的增加,每一个抽象类都需要一个子类实现,这样导致类的个数增加。
  • 类数量的增加,间接地增加了系统实现的复杂度。
  • 继承关系的自身缺点,如果父类增加新的抽象方法,所有子类都要改一遍。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public abstract class AbastractCourse {
public final void createCourse(){
//发布预习资料
postPreResource();
//制作课件
createPPT();
//直播授课
liveVideo();
//上传课后资料
postResource();
// 布置作业
postWork();
if (needCheckWork()){
checkWork();
}
}

protected boolean needCheckWork(){
return false;
}

protected abstract void checkWork();

protected void postPreResource() {
System.out.println("发布预习资料");
}
protected void createPPT() {
System.out.println("制作课件");
}
protected void liveVideo() {
System.out.println("直播授课");
}
protected void postResource() {
System.out.println("上传课后资料");
}
protected void postWork() {
System.out.println("布置作业");
}
}

public class JavaCourse extends AbastractCourse {
private boolean needCheckWork = false;

public void setNeedCheckWork(boolean needCheckWork) {
this.needCheckWork = needCheckWork;
}

@Override
protected boolean needCheckWork() {
return this.needCheckWork;
}

@Override
protected void checkWork() {
System.out.println("检查java作业");
}
}

public class PythonCourse extends AbastractCourse {
@Override
protected void checkWork() {
System.out.println("检查python作业");
}
}

测试:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args){
System.out.println("pattern----------------start");
JavaCourse java = new JavaCourse();
java.setNeedCheckWork(true);
java.createCourse();
System.out.println("pattern----------------Divider");
PythonCourse python = new PythonCourse();
python.createCourse();
System.out.println("pattern----------------end");
}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pattern----------------start
发布预习资料
制作课件
直播授课
上传课后资料
布置作业
检查java作业
pattern----------------Divider
发布预习资料
制作课件
直播授课
上传课后资料
布置作业
pattern----------------end

17.策略模式

策略模式(Strategy Pattern)又叫政策模式(Policy Pattern),它是将定义的算法家族,分别封装起来,让他们之间可以互相替换,从而让算法的变化不会影响到使用算法的用户。
可以避免多重分支的if…else…和switch语句。
属于行为型模式。

适用场景:

  • 假如系统中的很多类,而他们的区别仅仅在于他们的行为不同。
  • 一个系统需要动态地在几种算法中选择一种。
  • 需要屏蔽算法规则。

优点:

  • 策略模式符合开闭原则。
  • 避免使用多重条件转移语句,如if…else…和switch语句。
  • 使用策略模式可以提高算法的保密性和安全性。

缺点:

  • 客户端必须知道所有的策略,并且自行决定使用哪一个策略类。
  • 代码中会产生非常多策略类,增加维护难度。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public abstract class Payment {
public abstract String getPayName();
public MsgResult pay(String uid,double amount){
if (queryBalance(uid) < amount){
return new MsgResult(500,"支付失败","余额不足");
}
return new MsgResult(200,"支付成功","订单金额:"+amount);

}
protected abstract double queryBalance(String uid);
}

public class AliPay extends Payment {
@Override
public String getPayName() {
return "支付宝";
}
@Override
protected double queryBalance(String uid) {
return 5000;
}
}

public class JDPay extends Payment {
@Override
public String getPayName() {
return "京东";
}
@Override
protected double queryBalance(String uid) {
return 6000;
}
}

public class WechatPay extends Payment {
@Override
public String getPayName() {
return "微信";
}
@Override
protected double queryBalance(String uid) {
return 100;
}
}

public class UnionPay extends Payment {
@Override
public String getPayName() {
return "银联";
}
@Override
protected double queryBalance(String uid) {
return 10000;
}
}
//这里其实用到了委派模式
public class PayStrategy {
public static final String ALI_PAY="AliPay";
public static final String WECHAT_PAY="WechatPay";
public static final String JD_PAY="JDPay";
public static final String UNION_PAY="UnionPay";
public static final String DEFAULT_PAY=ALI_PAY;
private static Map<String,Payment> payStrategy = new HashMap<>();
static {
payStrategy.put(ALI_PAY,new AliPay());
payStrategy.put(WECHAT_PAY,new WechatPay());
payStrategy.put(JD_PAY,new JDPay());
payStrategy.put(UNION_PAY,new UnionPay());
}
public static Payment get(String payKey){
if (!payStrategy.containsKey(payKey)){
return payStrategy.get(DEFAULT_PAY);
}
return payStrategy.get(payKey);
}
}

public class Order {
public String uid;
public String orderId;
public double amount;
public Order(String uid, String orderId,double amount){
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
public MsgResult pay(){
return pay(PayStrategy.DEFAULT_PAY);
}
public MsgResult pay(String payKey){
Payment payment = PayStrategy.get(payKey);
System.out.println("支付方式:"+payment.getPayName());
System.out.println("交易金额:"+this.amount);
return payment.pay(uid,amount);
}
}

public class MsgResult {
public int code;
public Object data;
public String msg;
public MsgResult(int code,String msg,Object data){
this.code = code;
this.data = data;
this.msg = msg;
}

@Override
public String toString() {
return "MsgResult{" +
"code=" + code +
", data=" + data +
", msg='" + msg + '\'' +
'}';
}
}

测试:

1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args){
System.out.println("pattern----------------start");
Order order = new Order("1","12392847283753",230.87);
System.out.println(order.pay());
System.out.println(order.pay(PayStrategy.WECHAT_PAY));
System.out.println("pattern----------------end");
}
}

输出:

1
2
3
4
5
6
7
8
pattern----------------start
支付方式:支付宝
交易金额:230.87
MsgResult{code=200, data=订单金额:230.87, msg='支付成功'}
支付方式:微信
交易金额:230.87
MsgResult{code=500, data=余额不足, msg='支付失败'}
pattern----------------end

18.责任链模式

责任链模式(Chain of Responsibility Pattern),是将链中每一个节点看作一个对象,每个节点处理的请求均不同,且内部自动维护一个下一个节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。
属于行为型模式。

适用场景:

  • 多个对象可以处理同一请求,但是具体由哪个对象处理则在运行时动态决定。
  • 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  • 可动态指定一组对象处理请求。

优点:

  • 将请求与处理解耦。
  • 请求处理者(节点对象)只需要关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一级节点对象。
  • 具备链式传递处理请求功能,请求发送者无需知晓链路结构,只需要等待请求处理结果。
  • 链路结构灵活,可以通过改变链路结构动态的新增或删除责任。
  • 易于扩展新的请求处理类(节点对象),符合开闭原则。

缺点:

  • 责任链太长或处理时间过长,会影响整体性能。
  • 如果节点对象存在循环引用时,会造成死循环,导致系统崩溃。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public class Member {
public String loginName;
public String loginPass;
public String roleName;
public Member(String loginName,String loginPass){
this.loginName = loginName;
this.loginPass = loginPass;
}
}

public abstract class Handler<T> {
protected Handler next;
public void next(Handler next){
this.next = next;
};
public abstract void doHandler(Member member);

//建造者模式
public static class Builder<T> {
private Handler<T> head;
private Handler<T> tail;
public Builder<T> addHandler(Handler<T> handler){
if (this.head == null){
this.head = this.tail = handler;
return this;
}
this.tail.next(handler);
this.tail = handler;
return this;
}
public Handler<T> build(){
return this.head;
}
}
}

public class ValidateHandler extends Handler {
@Override
public void doHandler(Member member) {
if(StringUtils.isEmpty(member.loginName) || StringUtils.isEmpty(member.loginPass)){
System.out.println("用户名或密码为空");
return;
}
System.out.println("用户名或密码不为空,往下执行");
next.doHandler(member);
}
}

public class LoginHandler extends Handler {
@Override
public void doHandler(Member member) {
System.out.println("登录成功");
member.roleName = "管理员";
System.out.println("用户名或密码不为空,往下执行");
next.doHandler(member);
}
}

public class AuthHandler extends Handler{
@Override
public void doHandler(Member member) {
if(!"管理员".equals(member.roleName)){
System.out.println("你不是管理员,不能操作");
return;
}
System.out.println("允许操作");
}
}

public class MemberChain {
public void login(String loginName,String loginPass){
//通用模式

/*ValidateHandler validateHandler = new ValidateHandler();
LoginHandler loginHandler = new LoginHandler();
AuthHandler authHandler = new AuthHandler();
validateHandler.next(loginHandler);
loginHandler.next(authHandler);
validateHandler.doHandler(new Member(loginName,loginPass));*/

//和建造者模式结合使用
Handler.Builder builder = new Handler.Builder();
builder.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler());
builder.build().doHandler(new Member(loginName,loginPass));
}
}

测试:

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
System.out.println("pattern----------------start");
MemberChain memberChain = new MemberChain();
memberChain.login("tim","123");
System.out.println("pattern----------------end");
}
}

输出:

1
2
3
4
5
6
pattern----------------start
用户名或密码不为空,往下执行
登录成功
用户名或密码不为空,往下执行
允许操作
pattern----------------end

19.迭代器模式

迭代器模式(Iterator Pattern),又称为游标(Cursor Pattern)模式,它提供一种顺序访问集合、容器对象元素的方法,而又无须暴漏集合内部表示。
本质:抽离集合对象迭代行为到迭代器中,提供一致访问接口。
属于行为型模式。
适用场景:

  • 访问一个集合对象的内容而无需暴露它的内部表示。
  • 为遍历不同的集合结构提供一个统一的访问接口。

优点:

  • 多态迭代:为不同的聚合结构提供一致的遍历接口,即一个迭代接口可以访问不同的聚集对象。
  • 简化集合对象接口:迭代器模式将集合对象本身应该提供的元素迭代接口抽取到了迭代器中,使集合对象无须关心具体迭代行为。
  • 元素迭代功能多样化:每个集合对象都可以提供一个或多个不同的迭代器,使得同种元素聚合结构可以有不同的迭代行为。
  • 接口迭代与集合:迭代器模式封装了具体的迭代算法,迭代算法的变化,不会影响到集合对象的架构。

缺点:

  • 对于比较简单的遍历(数组或者有序列表),使用迭代器方式遍历较为繁琐。

迭代器比较简单啊,而且jdk已经给实现了,下面手写一个迭代器,看下迭代器原理啊:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public interface Iterator<E> {
E next();
boolean hasNext();
}
public class IteratorImpl<E> implements Iterator<E>{
private List<E> list;
private int cursor;
private E element;

public IteratorImpl( List<E> list){
this.list= list;
}

@Override
public E next() {
System.out.println("当前位置:"+cursor);
element = list.get(cursor);
cursor++;
return element;
}

@Override
public boolean hasNext() {
if (cursor > list.size()-1){
return false;
}
return true;
}
}

public interface ICourseAggreagte {
void add(Course course);
void remove(Course course);
Iterator<Course> iterator();
}
public class CourseAggreagteImpl implements ICourseAggreagte {
private List courseList;
public CourseAggreagteImpl(){
this.courseList = new ArrayList();
}
@Override
public void add(Course course) {
courseList.add(course);
}

@Override
public void remove(Course course) {
courseList.remove(course);
}

@Override
public Iterator<Course> iterator() {
return new IteratorImpl<Course>(courseList);
}
}

public class Course {
public String name;
public Course(String name){
this.name = name;
}
}

测试:

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
public class Test {
public static void main(String[] args) {
System.out.println("pattern----------------start");
Course java = new Course("java");
Course py = new Course("python");
Course js = new Course("js");
Course ai = new Course("ai");

ICourseAggreagte aggreagte = new CourseAggreagteImpl();
aggreagte.add(java);
aggreagte.add(py);
aggreagte.add(js);
aggreagte.add(ai);

System.out.println("========课程列表========");
printCourser(aggreagte);

aggreagte.remove(js);
System.out.println("========课程列表-删除js========");
printCourser(aggreagte);
System.out.println("pattern----------------end");

}

private static void printCourser(ICourseAggreagte aggreagte){
Iterator<Course> iterator = aggreagte.iterator();
while(iterator.hasNext()){
Course coure = iterator.next();
System.out.println("("+coure.name+")");
}

}
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pattern----------------start
========课程列表========
当前位置:0
(java)
当前位置:1
(python)
当前位置:2
(js)
当前位置:3
(ai)
========课程列表-删除js========
当前位置:0
(java)
当前位置:1
(python)
当前位置:2
(ai)
pattern----------------end

20.命令模式

命令模式(Command Pattern)是对命令的封装,每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式解耦了请求方和接收方,请求方只需请求执行命令,不用关心命令是怎样被接收,怎样被操作以及是否被执行等等。
本质:解耦命令请求与处理。
属于行为型模式。

适用场景:

  • 现实语义中具备命令的操作(如命令菜单,shell命令。。。。。)。
  • 请求调用者和请求的接收者需要解耦,使得调用者和接收者不直接交互。
  • 需要抽象出等待执行的行为,比如撤销操作和恢复操作等等。
  • 需要支持命令宏(即命令组合操作)。

优点:

  • 通过引入中间件(抽象接口),解耦了命令请求与实现。
  • 扩展性良好,可以很容的增加新命令。
  • 支持组合命令,支持命令队列。
  • 可以在现有命令的基础上,增加额外功能。

缺点:

  • 具体命令类可能很多。
  • 增加了程序的复杂度,理解更加困难。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public interface ICommand {
void execute();
}
public class PlayCommand implements ICommand{
private Gplayer gplayer;
public PlayCommand(Gplayer gplayer){
this.gplayer = gplayer;
}
@Override
public void execute() {
this.gplayer.paly();
}
}
public class PauseCommand implements ICommand{
private Gplayer gplayer;
public PauseCommand(Gplayer gplayer){
this.gplayer = gplayer;
}
@Override
public void execute() {
this.gplayer.pause();
}
}

public class SpeedCommand implements ICommand{
private Gplayer gplayer;
public SpeedCommand(Gplayer gplayer){
this.gplayer = gplayer;
}
@Override
public void execute() {
this.gplayer.speed();
}
}
public class StopCommand implements ICommand{
private Gplayer gplayer;
public StopCommand(Gplayer gplayer){
this.gplayer = gplayer;
}
@Override
public void execute() {
this.gplayer.stop();
}
}

//播放器
public class Gplayer {
public void paly(){
System.out.println("播放");
}
public void speed(){
System.out.println("滑动");
}
public void stop(){
System.out.println("停止");
}
public void pause(){
System.out.println("暂停");
}
}
//控制条?人
public class Controller {
private List<ICommand> commons = new ArrayList<>();
public void execute(ICommand command){
command.execute();
}

public void addCommond(ICommand command){
commons.add(command);
}
public void executes(){
for(ICommand command: commons){
command.execute();
}
commons.clear();
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args){
System.out.println("pattern----------------start");
Gplayer gplayer = new Gplayer();
Controller controller = new Controller();
controller.execute(new PlayCommand(gplayer));
System.out.println("=======执行组合擦欧总=========");
controller.addCommond(new PlayCommand(gplayer));
controller.addCommond(new PauseCommand(gplayer));
controller.addCommond(new SpeedCommand(gplayer));
controller.addCommond(new StopCommand(gplayer));
controller.executes();
System.out.println("pattern----------------end");
}

输出:

1
2
3
4
5
6
7
8
pattern----------------start
播放
=======执行组合擦欧总=========
播放
暂停
滑动
停止
pattern----------------end

21.状态模式

状态模式(State Pattern),也称为状态机(State Machine Pattern)模式,是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
属于行为模式。

适用场景:

  • 行为随状态改变而改变的场景。
  • 一个操作中含有庞大的多分支结构,并且这些分支取决于对象的状态。

优点:

  • 结构清晰:将状态独立为类,消除了冗余的if else 或者switch语句,使代码更加简洁,提高系统的可维护性。
  • 将状态转换显示化:通常的对象内部都是使用数值类型来定义状态,状态切换是通过赋值进行表现,不够直观;而使用状态类,在切换状态时,是以不同的类进行表示,转换目的更加明确。
  • 状态类职责明确且具备扩展性。

缺点:

  • 类膨胀:如果一个事务具备多种状态,则会造成状态类太多。
  • 状态模式的结构与实现较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对开闭原则的支持不太好,对于可以切换状态的状态模式,增加新的的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源代码。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public abstract class UserState {
protected AppContext context;

public void setContext(AppContext context) {
this.context = context;
}

public abstract void favorite();
public abstract void comment(String comment);
}

public class LoginState extends UserState {
@Override
public void favorite() {
System.out.println("收藏成功");
}

@Override
public void comment(String comment) {
System.out.println("评论成功:"+comment);
}
}

public class UnLoginState extends UserState {
@Override
public void favorite() {
this.jumpLoin();
this.context.getCurrentUserState().favorite();
}

@Override
public void comment(String comment) {
this.jumpLoin();
this.context.getCurrentUserState().comment(comment);
}

private void jumpLoin() {
System.out.println("未登录,跳转登录页");
this.context.setState(this.context.STATE_LOGIN);

}
}

public class AppContext {
public static final UserState STATE_LOGIN = new LoginState();
public static final UserState STATE_UNLOGIN = new UnLoginState();
private UserState currentUserState = STATE_UNLOGIN;

{
STATE_LOGIN.setContext(this);
STATE_UNLOGIN.setContext(this);
}

public void setState(UserState userState){
this.currentUserState = userState;
}

public UserState getCurrentUserState() {
return currentUserState;
}

public void favorite(){
this.currentUserState.favorite();
}

public void comment(String comment) {
this.currentUserState.comment(comment);
}
}

测试:

1
2
3
4
5
6
7
public static void main(String[] args){
System.out.println("pattern----------------start");
AppContext context = new AppContext();
context.favorite();
context.comment("评论");
System.out.println("pattern----------------end");
}

输出:

1
2
3
4
5
pattern----------------start
未登录,跳转登录页
收藏成功
评论成功:评论
pattern----------------end

其中最经典的状态机模式,还是Android中页面的生命周期。

22.备忘录模式

备忘录模式(Memento Pattern),又称为快照模式(Snapshot Pattern)或令牌模式(Token Pattern),是指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
特征:后悔药
属于行为型模式。

适用场景:

  • 需要保存历史快照的场景。
  • 希望在对象之外保存状态,且除了自己其他类对象无法访问状态保存具体内容。

优点:

  • 简化发起人实体类(Originator)职责,隔离状态存储与获取,实现了信息的封装,客户端无须关心状态的保存细节。
  • 提供状态回滚功能。

缺点:

  • 消耗资源:如果需要保存的状态过多时,每一次保存都会消耗很多内存。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//快照,备忘录
public class ArticleMemento {
public String title;
public String content;
public String imgs;
public ArticleMemento(String title,String content,String imgs){
this.title = title;
this.content = content;
this.imgs = imgs;
}

@Override
public String toString() {
return "ArticleMemento{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}

//草稿箱
public class DraftsBox {
//容器,存文章快照:压栈
private final Stack<ArticleMemento> stack = new Stack<>();
public void addMemento(ArticleMemento articleMemento){
stack.push(articleMemento);
}
public ArticleMemento getMemento(){
return stack.pop();
}
}

//发起人
public class Editor {
public String title;
public String content;
public String imgs;
public Editor(String title,String content,String imgs){
this.title = title;
this.content = content;
this.imgs = imgs;
}
public ArticleMemento saveToMemento(){
ArticleMemento articleMemento = new ArticleMemento(this.title,this.content,this.imgs);
return articleMemento;
}
public void undoFromMenento(ArticleMemento articleMemento){
this.title = articleMemento.title;
this.content = articleMemento.content;
this.imgs = articleMemento.imgs;
}

@Override
public String toString() {
return "Editor{" +
"title='" + title + '\'' +
", content='" + content + '\'' +
", imgs='" + imgs + '\'' +
'}';
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args){
System.out.println("pattern----------------start");
DraftsBox draftsBox = new DraftsBox();
Editor editor = new Editor("文章标题","文章内容","文章图片");
ArticleMemento articleMemento = editor.saveToMemento();
draftsBox.addMemento(articleMemento);
System.out.println("文章内容\n"+editor+"\n保存成功");
System.out.println("========修改文章1========");
editor.title = "文章标题1";
editor.content = "文章内容1";
editor.imgs = "文章图片1";
articleMemento = editor.saveToMemento();
draftsBox.addMemento(articleMemento);
System.out.println("文章内容\n"+editor+"\n保存到草稿箱1成功");
System.out.println("========撤销文章1========");
articleMemento = draftsBox.getMemento();
editor.undoFromMenento(articleMemento);
System.out.println("文章内容\n"+editor+"\n撤销完成");
System.out.println("========撤销文章2========");
articleMemento = draftsBox.getMemento();
editor.undoFromMenento(articleMemento);
System.out.println("文章内容\n"+editor+"\n撤销完成");
System.out.println("pattern----------------end");
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pattern----------------start
文章内容
Editor{title='文章标题', content='文章内容', imgs='文章图片'}
保存成功
========修改文章1========
文章内容
Editor{title='文章标题1', content='文章内容1', imgs='文章图片1'}
保存到草稿箱1成功
========撤销文章1========
文章内容
Editor{title='文章标题1', content='文章内容1', imgs='文章图片1'}
撤销完成
========撤销文章2========
文章内容
Editor{title='文章标题', content='文章内容', imgs='文章图片'}
撤销完成
pattern----------------end

23.中介者模式

中介者模式(Mediator Pattern)又称为调解者模式或调停者模式。用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
核心:通过中介者解耦系统各层次对象的直接耦合,层次对象的对外依赖通信统统交由中介者转发。
属于行为型模式。

适用场景:

  • 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
  • 交互的公共行为,如果需要改变行为则可以增加新的中介者类。

优点:

  • 减少类间依赖,将多对多依赖转化成了一对多,降低了类间耦合。
  • 类间各司其职,符合迪米特法则。

缺点:

  • 中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系西,当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。
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
public class User {
public String name;
private ChatRoom chatRoom;
public User(String name,ChatRoom chatRoom){
this.name = name;
this.chatRoom = chatRoom;
}
public void sendMessage(String msg){
this.chatRoom.showMessage(this,msg);
}
}
//中介者
public class ChatRoom {
public void showMessage(User user,String msg){
System.out.println("["+user.name+"]"+msg);
}
}

public static void main(String[] args){
System.out.println("pattern----------------start");
ChatRoom chatRoom = new ChatRoom();
User zhang = new User("漳卅",chatRoom);
User li = new User("里斯",chatRoom);
zhang.sendMessage("你好,里斯");
li.sendMessage("你好,漳卅");
System.out.println("pattern----------------end");
}

输出:

1
2
3
4
pattern----------------start
[漳卅]你好,里斯
[里斯]你好,漳卅
pattern----------------end

24.解释器模式

解释器模式(Interpreter Pattern)给定一个语言,定义它的文法的一种表示,并定一个解释器,这个解释器使用该表示来解释语言中的句子。
特征:为了解释一种语言,而为语言创建的解释器。
属于行为型模式。

适用场景:

  • 一些重复出现的问题可以用一种简单的语言来进行表达。
  • 一个简单的语法需要解释的场景。

优点:

  • 扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需要修改相应的非终结符表达式即可;若扩展语法时,只需添加相应非终结符类即可。
  • 增加了新的解释表达式的方式。
  • 易于实现文法:解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式。

缺点:

  • 语法规则比较复杂时,会引起类膨胀。
  • 执行效率比较低。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//数学表达式抽象
public interface IArithmeticInterpreter {
int interpreter();
}
//终结表达式
public class NumberInterpreter implements IArithmeticInterpreter{
private int value;
public NumberInterpreter(int value){
this.value = value;
}
@Override
public int interpreter() {
return this.value;
}
}
//非终结表达式
public abstract class Interpreter implements IArithmeticInterpreter {
protected IArithmeticInterpreter left;
protected IArithmeticInterpreter right;
public Interpreter(IArithmeticInterpreter left,IArithmeticInterpreter right){
this.left = left;
this.right = right;
}
}
//加法
public class AddInterpreter extends Interpreter{
public AddInterpreter(IArithmeticInterpreter left,IArithmeticInterpreter right){
super(left,right);
}
@Override
public int interpreter() {
return this.left.interpreter()+this.right.interpreter();
}
}
//减法
public class SubInterpreter extends Interpreter{
public SubInterpreter(IArithmeticInterpreter left, IArithmeticInterpreter right){
super(left,right);
}
@Override
public int interpreter() {
return this.left.interpreter()-this.right.interpreter();
}
}
//解释器类
public class Calculate {
private Stack<IArithmeticInterpreter> stack = new Stack<>();
public Calculate(String expression){
parse(expression);
}
private void parse(String expression){
String[] elements = expression.split(" ");
IArithmeticInterpreter left,right;
for(int i=0;i < elements.length;i++){
String ope = elements[i];
if("+".equals(ope)){
left = this.stack.pop();
right = new NumberInterpreter(Integer.valueOf(elements[++i]));
System.out.println("出栈:left:"+left.interpreter() + " right:"+right.interpreter());
this.stack.push(getInterpreter(left,right));
System.out.println("应用运算符:" + ope);
}else{
NumberInterpreter numberInterpreter = new NumberInterpreter(Integer.valueOf(ope));
this.stack.push(numberInterpreter);
System.out.println("入栈:"+numberInterpreter.interpreter());
}
}
}
public int calculate(){
return this.stack.pop().interpreter();
}

private Interpreter getInterpreter(IArithmeticInterpreter left,IArithmeticInterpreter right){
return new AddInterpreter(left,right);
}
}

public static void main(String[] args){
System.out.println("pattern----------------start");
System.out.println("result:"+new Calculate("10 + 30").calculate());
System.out.println("pattern----------------end");
}

输出:

1
2
3
4
5
6
pattern----------------start
入栈:10
出栈:left:10 right:30
应用运算符:+
result:40
pattern----------------end

25.观察者模式

观察者模式(Observer Pattern),又叫发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听器(Source-Listener)模式或从属者(Dependents)模式。定义一种一对多的依赖关系,一个主题对象可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被自动更新。
属于行为型模式。

适用场景:

  • 当一个抽象模型包含两个方面内容,其中一个方面依赖于另一个方面。
  • 其他一个或多个对象的变化依赖于另一个对象的变化。
  • 实现类似广播机制的功能,无需知道具体收听者,只需分发广播,系统中感兴趣的对象会自动接受该广播。
  • 多层级嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知。

优点:

  • 观察者和被观察者是松耦合的,符合依赖倒置原则。
  • 分离了表示层(观察者)和数据逻辑层(被观察者),并且建立了一套触发机制,使得数据的变化可以响应到多个表示层上。
  • 实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知。

缺点:

  • 如果观察者数量过多,则事件通知会耗时较长。
  • 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件。
  • 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃。

26.访问者模式

访问者模式(Visitor Pattern),是一种将数据结构与数据操作分离的设计模式。是指封装一些作用于某种数据结构中的各元素的操作。

特征:
可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
属于行为型模式。

适用场景:

  • 数据结构稳定,作用于数据结构的操作经常变化的场景。
  • 需要数据结构与数据操作分离的场景。
  • 需要对不同数据类型(元素)进行操作,而不适用分支判断具体类型的场景。

优点:

  • 解耦了数据结构与数据操作,使得操作集合可以独立变化。
  • 扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作。
  • 元素具体类型并非单一,访问者均可操作。
  • 各角色职责分离,符合单一职责原则。

缺点:

  • 无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进来,则访问者类必须增加对应元素类型的操作,违背了开闭原则。
  • 具体元素变得更加困难:具体元素增加属性,删除属性等操作会导致对应的访问者类需要进行相应的修改,尤其当有大量访问者类时,修改范围太大。
  • 违背依赖倒置原则:为了达到”区别对待“,访问者依赖的是具体元素类型而不是抽象。

参考资料

从未见过如此通俗易懂的Java设计模式教程