首页 > 技术文章 > 23种设计模式?一目了然!

snzigod 2019-06-08 13:59 原文

23种设计模式?一目了然!

设计模式之间的关系

设计模式分类

创建型模式(5种)

处理对象创建,试图根据实际情况使用合适的方式创建对象。

  • 将系统使用的具体类封装。
  • 隐藏具体类实例创建和组合方式。

包含:工厂方法模式抽象工厂模式单例模式建造者模式原型模式

结构型模式(7种)

借由一以贯之的方式来了解元件间的关系,以简化设计。

包含:适配器模式装饰器模式代理模式外观模式桥接模式组合模式享元模式

行为型模式(11种)

用来识别对象之间的常用交流模式并加以实现。

包含:策略模式模板方法模式观察者模式迭代子模式责任链模式命令模式备忘录模式状态模式访问者模式中介者模式解释器模式

其它(2种)

并发型模式线程池模式

设计模式六大原则(SOLID)

单一职责原则(Single Responsibility Principle)

一个类只负责一个职责

  • 代码的粒度降低了,类的复杂度降低了。
  • 可读性提高了,每个类的职责都很明确,可读性自然更好。
  • 可维护性提高了,可读性提高了,一旦出现 bug ,自然更容易找到他问题所在。
  • 改动代码所消耗的资源降低了,更改的风险也降低了。

开闭原则(Open Closed Principle)

对扩展开放,对修改关闭

在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。

实现“开-闭”原则的关键步骤就是抽象化。

里氏代换原则(Liskov Substitution Principle)

面向对象设计的基本原则之一。

任何基类可以出现的地方,子类一定可以出现

LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

里氏代换原则是对“开-闭”原则的补充。

基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

迪米特法则(最少知道原则)(Law of Demeter)

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

接口隔离原则(Interface Segregation Principle)

使用多个隔离的接口,比使用单个接口要好

降低依赖,降低耦合。

依赖倒置原则(Dependence Inversion Principle)

开闭原则的基础。

针对接口编程,依赖于抽象而不依赖于具体

设计模式(23种)

工厂方法模式(Factory Method)

核心精神是封装类中变化的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。

核心结构有四个角色,分别是工厂;抽象产品;具体产品。

public interface ShirtProducer {

	void produceShirt();

}
public class ManShirtProducer implements ShirtProducer {

	@Override
	public void produceShirt() {
		System.out.println("produce man shirt.");
	}

}
public class WomanShirtProducer implements ShirtProducer {

	@Override
	public void produceShirt() {
		System.out.println("produce woman shirt.");
	}

}

简单工厂模式

建立一个工厂类,对实现了同一接口的一些类进行实例的创建

public class SimpleShirtFactory {

	public ShirtProducer produce(String shirtType) {
		if ("t-shirt".equals(shirtType)) {
			return new WomanShirtProducer();
		} else {
			return new ManShirtProducer();
		}
	}

}

简单工厂(反射)

简单工厂的改进,通过反射实现对新增产品生产的兼容,新增产品生产无需改写工厂类。

public class ReflectSimpleShirtFactory {

	public ShirtProducer produce(String shirtType)
			throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		Class<?> ShirtProducer = Class.forName(shirtType);
		return (ShirtProducer) ShirtProducer.newInstance();
	}

}

多个工厂方法模式

对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象

public class MultiShirtFactory {

	public ShirtProducer produceManShirt() {
		return new ManShirtProducer();
	}

	public ShirtProducer produceWomanShirt() {
		return new WomanShirtProducer();
	}

}

静态工厂方法模式

将多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可

public class StaticMultiShirtFactory {

	public static ShirtProducer produceShirt() {
		return new ManShirtProducer();
	}

	public static ShirtProducer produceTShirt() {
		return new WomanShirtProducer();
	}

}
凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。

大多数情况下,选用静态工厂方法模式。
public class ShirtFactoryMain {

	public static void main(String[] args) {
		System.out.println("普通工厂模式");
		SimpleShirtFactory shirtFactory1 = new SimpleShirtFactory();
		ShirtProducer shirtProducer1 = shirtFactory1.produce("shirt");
		shirtProducer1.produceShirt();

		try {
			System.out.println("普通工厂模式(反射)");
			ReflectSimpleShirtFactory shirtFactory11 = new ReflectSimpleShirtFactory();
			ShirtProducer shirtProducer11 = shirtFactory11.produce(ManShirtProducer.class.getName());
			shirtProducer11.produceShirt();
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}

		System.out.println("多工厂方法模式");
		MultiShirtFactory shirtFactory2 = new MultiShirtFactory();
		ShirtProducer shirtProducer2 = shirtFactory2.produceManShirt();
		shirtProducer2.produceShirt();

		System.out.println("静态工厂方法模式");
		ShirtProducer shirtProducer3 = StaticMultiShirtFactory.produceShirt();
		shirtProducer3.produceShirt();
	}

}

抽象工厂模式(Abstract Factory)

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。

抽象工厂模式,工厂方法模式的抽象化,创建多个工厂类,需要增加新的功能时,直接增加新的工厂类即可,不需要修改之前的代码。

核心结构有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品。

  • 抽象工厂

抽象工厂模式核心,与应用程序无关,任何在模式中创建的对象的工厂类必须实现这个接口。

  • 具体工厂

实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。

  • 抽象产品

工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。

  • 具体产品

实现了抽象产品角色所定义的接口。

public interface ShirtFactory {

	ShirtProducer produce();

}
public class MailSenderFactory implements SenderFactory {
    
    @Override
    public Sender produce() {
        return new MailSender();
    }
}
public class ManShirtFactory implements ShirtFactory {

	@Override
	public ShirtProducer produce() {
		return new ManShirtProducer();
	}

}
public class WomanShirtFactory implements ShirtFactory {

	@Override
	public ShirtProducer produce() {
		return new WomanShirtProducer();
	}

}
public class ShirtProducerMain {

	public static void main(String[] args) {
		System.out.println("抽象工厂模式");
		ShirtFactory shirtProvider1 = new ManShirtFactory();
		ShirtProducer shirtProducer1 = shirtProvider1.produce();
		shirtProducer1.produceShirt();

		ShirtFactory shirtProvider2 = new WomanShirtFactory();
		ShirtProducer shirtProducer2 = shirtProvider2.produce();
		shirtProducer2.produceShirt();
	}

}

建造者模式(Builder)

将各种产品集中起来进行管理,用来创建复合对象。

工厂模式关注创建单个产品,建造者模式关注创建符合对象、多个部分。

public class ShirtProducerBuilder {

	private List<ShirtProducer> shirtProducerlist = new LinkedList<ShirtProducer>();

	public void produceManShirtProducer(int count) {
		for (int i = 0; i < count; i++) {
			shirtProducerlist.add(new ManShirtProducer());
		}
	}

	public void produceWomanShirtProducer(int count) {
		for (int i = 0; i < count; i++) {
			shirtProducerlist.add(new WomanShirtProducer());
		}
	}

}public class SenderBuilder {
    private LinkedList<Sender> senderList = new LinkedList<Sender>();
    
    public void buildMailSender(int count) {
        for(int i; i < count; i++) {
            senderList.add(new MailSender());
        }
    }
    
    public void buildSmsSender(int count) {
        for(int i; i < count; i++) {
            senderList.add(new SmsSender());
        }
    }
}
public class ShirtProducerBuilderMain {

	public static void main(String[] args) {
		ShirtProducerBuilder builder = new ShirtProducerBuilder();
		builder.produceManShirtProducer(10);
		builder.produceWomanShirtProducer(20);
	}

}

单例模式(Singleton)

Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。

懒汉单例模式

第一次调用时实例化,并发环境下可能出现多个相同对象,线程是不安全

public class Singleton {
    private Singleton() {}
    private static Singleton singleton = null;
    
    public static Singleton getSingleton() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
线程安全懒汉单例模式

synchronized关键字锁住对象,性能上会有所下降。

public class Singleton {
    private Singleton() {}
    private static Singleton singleton = null;
    
    public static synchronized Singleton getSingleton() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
双重检查锁定(DCL)

Double Checked locking,synchronized关键字加在内部,在instance为null,并创建对象的时才需要加锁,性能有一定的提升。

public class Singleton {
    private Singleton() {}
    private volatile static Singleton singleton = null;
    
    public static Singleton getSingleton() {
        if(singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
在Java指令中创建对象和赋值操作是分开进行的。
instance = new Singleton();语句是分两步执行的。JVM并不保证这两个操作的先后顺序。有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例,可能出错。

a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

要解决也十分简单,加上我们的volatile关键字就可以了,volatile有内存屏障的功能!

对 volatile 变量的写操作,不允许和它之前的读写操作打乱顺序;对 volatile 变量的读操作,不允许和它之后的读写乱序。

Volatile变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile变量的最新值。Volatile变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。

正确使用 volatile 变量的条件:
对变量的写操作不依赖于当前值。
该变量没有包含在具有其他变量的不变式中。
静态内部类

初始化静态数据时,Java提供了的线程安全性保证

public class Singleton {
    
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    private Singleton() {}
    
    public static final Singleton getSingleton() {
        return LazyHolder.INSTANCE;
    }
}

饿汉单例模式

类初始化时,已经自行实例化一个静态对象,线程安全。

public class Singleton {
    private Singleton() {}
    private static final Singleton singleton = new Singleton();
    
    public static Singleton getSingleton() {
        return singleton;
    }
}

枚举单例模式

最安全、简洁的写法。

public enum EnumSingleton {
	EnumSingleton;

	public void singleton() {
		System.out.println("this is a singleton method!");
	}
}

登记单例模式

通过一个专门的类对各单例模式的单一实例进行管理和维护。

public class Singleton {
    private static Map<String, Singleton> map = new HashMap<String, Singleton>();
    
    static {
		Singleton singleton = new Singleton();
		map.put(singleton.getClass().getName(), singleton);
	}
    
    private Singleton() {}
    private static Singleton singleton = null;
    
    public static Singleton getSingleton(String name) {
        return map.get(name);
    }
    
	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance("com.Singleton");
	}
}
单例模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

原型模式(Prototype)

将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

public class Prototype implements Cloneable {
    
    /* 浅复制 */
    public Object clone throws CloneNotSupportedException {
        Prototype prototype = (Prototype) super.clone();
        return prototype;
    }
    
    /* 深复制 */
    public Object deepClone() throws IOException, ClassNotFoundException {
        /* 写入当前对象的二进制流 */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        
        /* 读出二进制流产生的新对象 */
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
}
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

适配器模式(Adapter)

将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

类的适配器

当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

public class Source {
    
    public void method1() {
        System.out.println("this is original method!");
    }
}
public interface Targetable {
    
    /* 原方法 */
    public void method1();
    /* 新方法 */
    public void method2();
}
public class Adapter extends Source implements Targetable {
    
    @Override
    public void method2() {
        System.out.println("this is targetable method!");
    }
}
public class AdapterTest {
    
    public static void main(String[] args) {
        Targetable target = new Adapter();
        target.method1();
        target.method2();
    }
}

对象的适配器

当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

基本思路和类的适配器模式相同,只是将Adapter类作修改,不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题

public class Wrapper implements Targetable {
    
    private Source source;
    
    public Adapter(Source source) {
        super();
        this.source = source;
    }
    
    @Override
    public void method1() {
        this.method1();
    }
    
    @Override
    public void method2() {
        System.out.println("this is targetable method!");
    }
}
public class AdapterTest {
    
    public static void main(String[] args) {
        Source source = new Source();
        Targetable target = new Wrapper(source);
        target.method1();
        target.method2();
    }
}

接口的适配器

不希望实现一个接口中所有方法时,可以创建一个抽象类Wrapper实现所有方法,写别的类时继承抽象类即可

public interface Sourceable {
    
    void method1();    
    void method2();
}
public abstract class Wrapper implements Sourceable {
    
    public void method1() {}
    public void method2() {}
}
public class SourceSub1 extends Wrapper {
    
    public void method1() {
        System.out.println("this is SourceSub1!");
    }
}
public class SourceSub2 extends Wrapper {
    
    public void method2() {
        System.out.println("this is SourceSub2!");
    }
}
public class WrapperTest {
    Sourceable source1 = new SourceSub1();
    Sourceable source2 = new SourceSub2();
    
    source1.method1();
    source1.method2();
    source2.method1();
    source2.method2();
}
在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。

装饰器模式(Decorator)

给一个对象动态增加一些新功能,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象实例

  • 需要扩展一个类的功能。
  • 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
public interface Sourceable {
    
    void method();
}
public class Source implements Sourceable {
    
    @Override
    public void method() {
        System.out.println("this is original method!");
    }
}
public class Decorator implements Sourceable {
    
    private Sourceable source;
    
    public Decorator(Sourceable source){
        super();
        this.source = source;
    }
    
    @Override
    public void method() {
        System.out.println("before decorator!");
        source.method();
        System.out.println("after decorator!");
    }
}
public class DecoratorTest {
    
    public static void main(String[] args) {
        Sourceable originalSource = new Source();
        Sourceable source = new Decorator(originalSource);
        source.method();
    }
}
应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。

代理模式(Proxy)

多一个代理类出来,替原对象进行一些操作

public interface Sourceable {
    
    void method();
}
public class Source implements Sourceable {
    
    @Override
    public void method() {
        System.out.println("this is original method!");
    }
}
public class Proxy implements Sourceable {
    
    private Source source;
    public Proxy() {
        super();
        this.source = new Source();
    }
    
    @Override
    public void method() {
        before();
        source.method();
        after();
    }
    
    private void before() {
        System.out.println("before proxy!");
    }
    
    private void after() {
        System.out.println("after proxy!");
    }
}
public class ProxyTest {
    
    public static void main(String[] args) {
        Sourceable source = new Proxy();
        source.method();
    }
}
比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。

应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

外观模式(Facade)

解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低类类之间的耦合度

public class CPU {
    
    public void startup() {
        System.out.println("cpu startup!");
    }
    
    public void shutdown() {
        System.out.println("cpu shutdown!");
    }
}
public class Memory {
    
    public void startup() {
        System.out.println("memory startup!");
    }
    
    public void shutdown() {
        System.out.println("memory shutdown!");
    }
}
public class Disk {
    
    public void startup() {
        System.out.println("disk startup!");
    }
    
    public void shutdown() {
        System.out.println("disk shutdown!");
    }
}
public class Computer {
    
    private CPU cpu;
    private Memory memory;
    private Disk disk;
    
    public Computer() {
        cpu = new CPU();
        memory = new Memory();
        disk = new Disk();
    }
    
    public void startup() {
        cpu.startup();
        memory.startup();
        disk.startup();
        System.out.println("computer startup!");
    }
    
    public void shutdown() {
        cpu.shutdown();
        memory.shutdown();
        disk.shutdown();
        System.out.println("computer shutdown!");
    }
}
public class ComputerTest {
    
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.startup();
        computer.shutdown();
    }
}

组合模式(Composite)

又叫部分-整体模式,在处理类似树形结构的问题时比较方便

public class TreeNode {
    
    private String name;
    private TreeNode parent;
    private Vector<TreeNode> children = new vector<TreeNode>();
    
    public TreeNode(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public TreeNode getParent() {
        return parent;
    }
    
    public void setParent(TreeNode parent) {
        this.parent = parent;
    }
    
    public void add(TreeNode node) {
        children.add(node);
    }
    
    public void remove(TreeNode node) {
        children.remove(node);
    }
    
    public Enumeration<TreeNode> getChildren() {
        return children.elements();
    }
}
public class Tree {
    
    TreeNode root = null;
    
    public Tree(String name) {
        root = new TreeNode(name);
    }
    
    public static void main(String[] args) {
        Tree tree = new Tree("A");
        TreeNode nodeB = new TreeNode("B");
        TreeNode nodeB = new TreeNode("C");
        
        nodeB.add(nodeC);
        tree.root.add(nodeB);
    }
}
使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。

桥接模式(Bridge)

把事物和其具体实现分开,使他们可以各自独立的变化

将抽象化与实现化解耦,使得二者可以独立变化。

public interface Sourceable {
    
    void method();
}
public class SourceSub1 implements Sourceable {
    
    @Override
    public void method() {
        System.out.println("this is sub1!");
    }
}
public class SourceSub2 implements Sourceable {
    
    @Override
    public void method() {
        System.out.println("this is sub2!");
    }
}
public abstract class Bridge {
    private Sourceable source;
    
    public void method() {
        source.method();
    }
    
    public Sourceable getSource() {
        return source;
    }
    
    public void setSource(Sourceable source) {
        this.source = source;
    }
}
public class MyBridge extends Bridge {
    
    public void method() {
        getSource().method();
    }
}
public class BridgeTest {
    
    public static void main(String[] args) {
        Bridge bridge = new MyBridge();
        
        /* 调用第一个对象 */
        Sourceable source1 = new SourceSub1();
        bridge.setSource(source1);
        bridge.method();
        
        /* 调用第二个对象 */
        Sourceable source2 = new SourceSub2();
        bridge.setSource(source2);
        bridge.method();
    }
}
像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。

享元模式(Flyweight)

实现对象的共享,即共享池

当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

public class ConnectionPool {
    
    private Vector<> pool;
    
    private String url = "jdbc:mysql://localhost:3306/test";
    private String username = "root";
    private String password = "root";
    private String driverClassName = "com.mysql.jdbc.Driver";
    
    private int poolSize = 10;
    private static ConnectionPool instance = null;
    Connction conn = null;
    
    private ConnectionPool() {
        pool = new Vector<Connection>(poolSize);
        
        for (int i = 0; i < poolSize; i++) {
            try {
                Class.forName(driverClassName);
                conn = DriverManager.getConnection(url, username, password);
                pool.add(conn);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
    public synchronized void release() {
        pool.add(conn);
    }
    
    public synchronized Connection getConnection() {
        if (pool.size() > 0) {
            Connection conn = pool.get(0);
            pool.remove(conn);
            return conn;
        } else {
            return null;
        }
    }
}

策略模式(strategy)

定义一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户

  • 抽象策略
  • 具体策略
  • 环境角色
public interface ICalculator {
    
    public int calculate(String exp);
}
public abstract class AbstractCalculator {
    
    public int[] split(String exp, String opt) {
        String array[] = exp.split(opt);
        int arrayInt[] = new int[2];
        arrayInt[0] = Integer.parseInt(array[0]);
        arrayInt[1] = Integer.parseInt(array[1]);
        return arrayInt;
    }
}
public class Plus extends AbstractCalculator implements ICalculator {
    
    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "\\+");
        return arrayInt[0] + arrayInt[1];
    }
}
public class Minus extends AbstractCalculator implements ICalculator {
    
    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "\\-");
        return arrayInt[0] - arrayInt[1];
    }
}
public class Multiply extends AbstractCalculator implements ICalculator {
    
    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "\\*");
        return arrayInt[0] * arrayInt[1];
    }
}
public class StrategyTest {
    
    public static void main(String[] args) {
        String exp = "2 + 8";
        ICalculator cal = new Plus();
        cal.calculate(exp);
    }
}

模板方法模式(Template Method)

一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用

public abstract class AbstactCalculator {
    
    public final int calculate(String exp, String opt) {
        int array[] = split(exp, opt);
        return calculate(array[0], array[1]);  
    }
    
    public abstract int calculate (int num1, int num2);
    
    public int[] split(String exp, String opt) {
        String array[] = exp.split(opt);
        int arrayInt[] = new int[2];
        arrayInt[0] = Integer.parseInt(array[0]);
        arrayInt[1] = Integer.parseInt(array[1]);
        return arrayInt;
    }

}

public class Plus extends AbstractCalculator {
    
    @Override
    public int calculate(int num1, int num2) {
        return num1 + num2;
    }
}

public class StrategyTest {
    
    public static void main(String[] args) {
        String exp = "8+8";
        AbstractCalculator cal = new Plus();
        cal.calculate(exp, "\\+");
    }
}

观察者模式(Observer)

当一个对象变化时,依赖该对象的对象都会收到通知,并且随之变化。

对象之间是一种一对多的关系。

public interface Observer {
    
    void update();
}
public class Observer1 implements Observer {
    
    @Override
    public void update() {
        System.out.println("observer1 has received!");
    }
}
public class Observer2 implements Observer {
    
    @Override
    public void update() {
        System.out.println("observer2 has received!");
    }
}
public interface Subject {
    
    void add(Observer observer);
    
    void del(Observer observer);
    
    void notifyObservers();
    
    void operation();
}
public abstract class AbstractSubject implements Subject {
    
    private Vector<Observer> vector = new Vector<Observer>();
    
    @Override
    public void add(Observer observer) {
        vector.add(observer);
    }
    
    @Override
    public void del(Observer observer) {
        vector.remove(observer);
    }
    
    @Override
    public void notifyObservers() {
        Enumeration<Observer> enumo = vector.elements();
        while(enumo.hasMoreElements()) {
            enumo.nextElement().update();
        }
    }
}
public class MySubject extends AbstractSubject {
    
    @Override
    public void operation() {
        System.out.println("update self!");
        notifyObservers();
    }
}
public class ObserverTest {
    
    public static void main(String[] args) {
        Subject sub = new MySubject();
        sub.add(new Observer1());
        sub.add(new Observer2());
        
        sub.opration();
    }
}
观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。

迭代子模式(Iterator)

顺序访问聚集中的对象。

一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。

public interface Collection {
    
    Iterator iterator();
    
    Object get(int i);
    
    int size();
}
public interface Iterator {
    
    Object previous();
    Object next();
    boolean hasNext();
    Object first();
}
public class MyCollection implements Collection {
    
    public String string[] = {"A", "B", "C", "D", "E"};
    
    @Override
    public Iterator iterator() {
        return new MyIterator(this);
    }
    
    @Override
    public Object get(int i) {
        return string[i];
    }
    
    @Override
    public int size() {
        return string.length;
    }
}
public class MyIterator implements Iterator {
    
    private Collection collection;
    private int pos = -1;
    
    public MyIterator(Collection collection) {
        this.collection = collection;
    }
    
    @Override
    public Object previous() {
        if(pos > 0) {
            pos--;
        }
        return collection.get[pos];
    }
    
    @Override
    public Object next() {
        if(pos < collection.size() - 1) {
            pos++;
        }
        return collection.get(pos);
    }
    
    @Override
    public boolean hasNext() {
        if(pos < collection.size() - 1) {
            return true;
        } else {
            return false;
        }
    }
    
    @Override
    public Object first() {
        pos = 0;
        return collection.get(pos);
    }
}
public class Test {
    
    public static void main(String[] args) {
        Collection collection = new MyCollection();
        Iterator it = collection.iterator();
        
        while(it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

责任链模式(Chain of Responsibility)

有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求

责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。

public interface Handler {
    
    void operator();
}
public abstract class AbstractHandler {
    
    private Handler handler;
    
    public Handler getHandler() {
        return handler;
    }
    
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
}
public class MyHandler extends AbstractHandler implements Handler {
    
    private String name;
    
    public MyHandler(String name) {
        this.name = name;
    }
    
    @Override
    public void operator() {
        System.out.println(name + "deal!");
        if(getHandler() != null) {
            getHandler().operator();
        }
    }
}
public class Test {
    
    public static void main(String[] args) {
        MyHandler h1 = new MyHandler("h1");
        MyHandler h2 = new MyHandler("h2");
        MyHandler h3 = new MyHandler("h3");
        
        h1.setHandler(h2);
        h2.setHandler(h3);
        
        h1.operator();
    }
}
链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。

命令模式(Command)

目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开

  • Command:定义命令的统一接口。
  • ConcreteCommand:Command接口的实现者,用来执行具体的命令,某些情况下可以直接用来充当Receiver。
  • Receiver:命令的实际执行者。
  • Invoker:命令的请求者,是命令模式中最重要的角色。这个角色用来对各个命令进行控制。

命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。

Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想!

备忘录模式(Memento)

又叫做快照模式(Snapshot Pattern)或Token模式。

在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

public class Original {

	private String value;

	public Original(String value) {
		this.value = value;
	}

	public Memento createMemento() {
		return new Memento(this.value);
	}

	public void restoreMemento(Memento memento) {
		this.value = memento.getValue();
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

}
public class Memento {

	private String value;

	public Memento(String value) {
		this.value = value;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

}
public class Storage {

	private Memento memento;

	public Storage(Memento memento) {
		this.memento = memento;
	}

	public Memento getMemento() {
		return memento;
	}

	public void setMemento(Memento memento) {
		this.memento = memento;
	}

}
public class MementoMain {

	public static void main(String[] args) {
		//  创建原始类
		Original org = new Original("egg");

		//  创建备忘录
		Storage store = new Storage(org.createMemento());

		//  修改原始类的状态
		System.out.println("init state: " + org.getValue());
		org.setValue("egg1");
		System.out.println("modify state: " + org.getValue());

		//  恢复原始类的状态
		org.restoreMemento(store.getMemento());
		System.out.println("reset state: " + org.getValue());
	}

}
通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。

状态模式(State)

当对象的状态改变时,同时改变其行为

  • Context:它就是那个含有状态的对象,它可以处理一些请求,这些请求最终产生的响应会与状态相关。
  • State:状态接口,它定义了每一个状态的行为集合,这些行为会在Context中得以使用。
  • ConcreteState:具体状态,实现相关行为的具体状态类。
public interface CarState {

	void action();
}
public class StartCarState implements CarState {

	@Override
	public void action() {
		System.out.println("start the car.");
	}

}
public class RunCarState implements CarState {

	@Override
	public void action() {
		System.out.println("run the car.");
	}

}
public class StopCarState implements CarState {

	@Override
	public void action() {
		System.out.println("stop the car.");
	}

}
public class CarContext {

	private CarState carState;

	public CarState getCarState() {
		return carState;
	}

	public void setCarState(CarState carState) {
		this.carState = carState;
	}

	public void action() {
		carState.action();
	}

}
public class StateMain {

	public static void main(String[] args) {
		CarContext carContext = new CarContext();
		carContext.setCarState(new StartCarState());
		carContext.action();

		carContext.setCarState(new RunCarState());
		carContext.action();

		carContext.setCarState(new StopCarState());
		carContext.action();
	}

}
拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。

访问者模式(Visitor)

访问者模式是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果

public interface Visitor {

	void visit(Subject sub);

}
public class MyVisitor implements Visitor {

	@Override
	public void visit(Subject sub) {
		System.out.println("visit the subject: " + sub.getSubject());
	}

}
public interface Subject {

	void accept(Visitor visitor);

	String getSubject();

}
public class MySubject implements Subject {

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

	@Override
	public String getSubject() {
		return "love";
	}

}
public class VisitorMain {

	public static void main(String[] args) {
		Visitor visitor = new MyVisitor();
		Subject sub = new MySubject();
		sub.accept(visitor);
	}

}
该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:1、新功能会不会与现有功能出现兼容性问题?2、以后会不会再需要添加?3、如果类不允许修改代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦。

中介者模式(Mediator)

用来降低类类之间的耦合

有点像spring容器的作用。

public abstract class User {

	private Mediator mediator;

	public Mediator getMediator() {
		return mediator;
	}

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

	public User(Mediator mediator) {
		this.mediator = mediator;
	}

	abstract void work();

}
public class User1 extends User {

	public User1(Mediator mediator) {
		super(mediator);
	}

	@Override
	void work() {
		System.out.println("user1 exe!");
	}

}
public class User2 extends User {

	public User2(Mediator mediator) {
		super(mediator);
	}

	@Override
	void work() {
		System.out.println("user2 exe!");
	}

}
public interface Mediator {

	void createMediator();

	void workAll();

}
public class MyMediator implements Mediator {

	private User user1;
	private User user2;

	public User getUser1() {
		return user1;
	}

	public void setUser1(User user1) {
		this.user1 = user1;
	}

	public User getUser2() {
		return user2;
	}

	public void setUser2(User user2) {
		this.user2 = user2;
	}

	@Override
	public void createMediator() {
		user1 = new User1(this);
		user2 = new User2(this);
	}

	@Override
	public void workAll() {
		user1.work();
		user2.work();
	}

}
public class MediatorMain {

	public static void main(String[] args) {
		Mediator mediator = new MyMediator();
		mediator.createMediator();
		mediator.workAll();
	}

}

解释器模式(Interpreter)

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

public interface Expression {

    int interpret(Context context);
}
public class Plus implements Expression {
    
    @Override
    public int interpret(Context context) {
        return context.getNum1() + context.getNum2();
    }
}
public class Minus implements Expression {
    
    @Override
    public int interpret(Context context) {
        return context.getNum1() - context.getNum2();
    }
}
public class Context {
    
    private int num1;
    private int num2;
    
    public Context(int num1, int num2) {
        this.num1 = num1;
        this.num2 = num2;
    }
    
    public int getNum1() {
        return num1;
    }
    
    public void setNum1(int num1) {
        this.num1 = num1;
    }
    
    public int getNum2() {
        return num2;
    }
    
    public void setNum2(int num2) {
        this.num2 = num2;
    }
}
public class InterpreterMain {

	public static void main(String[] args) {
		System.out.println(new Minus().interpret(new Context(new Plus().interpret(new Context(12, 23)), 3)));
	}

}

推荐阅读