设计模式及案例

23种设计模式概要及易懂的例子

23种设计模式(分为三大类)

设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

为什么要使用设计模式?
为了代码复用,增加可维护性

设计模式的六大原则

1、开闭原则(Open Close Principle)

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则的基础,具体内容:针对对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

创建型模式(共五种)

单例模式

概念

保证一个类仅有一个实例,并提供一个访问它的全局访问点。
个人总结:在类内部创造单一对象,通过设置构造方法权限,使类外部无法再创造对象
注:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例

创建方式

主要使用懒汉和懒汉式
1、饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
2、懒汉式: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
3、静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
4、枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
5、双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

实例解析

1.饿汉式
该模式的特点是类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在了。饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

public class HungrySingleton {
    //类初始化时,会立即加载该对象,线程安全,调用效率高
    private static final HungrySingleton instance = new HungrySingleton();
    private HungrySingleton() {
    }
    public static HungrySingleton getInstance() {
        return instance;
    }
}

2.懒汉式
该模式的特点是类加载时没有生成单例,只有当第一次调用getlnstance方法时才去创建这个单例。

public class LazySingleton {
    private LazySingleton() { }//1.private 避免类在外部被实例化
    private static volatile LazySingleton instance = null;//2.保证instance在所有线程中同步
    public static LazySingleton getInstance() {//3
        if (instance == null) {//4第一次检查
            synchronized(LazySingleton.class){//5加锁
                if(instance == null){//6第二次检查
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

懒汉式和饿汉式区别

1、线程安全
饿汉式天生就是线程安全的,可以直接用于多线程。
懒汉式本身就非线程安全的,但是可以通过关键字volatilesynchronized来实现线程安全。
2、资源加载和性能
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

优缺点

优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
2、避免对资源的多重占用(比如写文件操作)。

缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景

1、要求生产唯一序列号。
2、WEB中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
3、创建的一个对象需要消耗的资源过多,比如I/O与数据库的连接等。注意事项:getInstance()方法中需要使用同步锁,synchronized (Singleton.class)防止多线程同时进入造成instance被多次实例化。

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字volatilesynchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。单例模式中volatile关键字的作用

拓展

单例模式可扩展为有限的多例(Multitcm)模式,这种模式可生成有限个实例并保存在ArrayList中,客户需要时可随机获取

工厂模式

概念

它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式。

好处

1、工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
2、利用工厂模式可以降低程序的耦合性,为后期的维护修改提供了很大的便利。
3、将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

Spring开发中的工厂设计模式

1.Spring IOC

  • 看过Spring源码就知道,在Spring IOC容器创建bean的过程是使用了工厂设计模式
  • Spring中无论是通过xml配置还是通过配置类还是注解进行创建bean,大部分都是通过简单工厂来进行创建的。
  • 当容器拿到了beanName和class类型后,动态的通过反射创建具体的某个对象,最后将创建的对象放到Map中。

2.为什么Spring IOC要使用工厂设计模式创建Bean呢

  • 在实际开发中,如果我们A对象调用B,B调用C,C调用D的话我们程序的耦合性就会变高。(耦合大致分为类与类之间的依赖,方法与方法之间的依赖。)
  • 在很久以前的三层架构编程时,都是控制层调用业务层,业务层调用数据访问层时,都是是直接new对象,耦合性大大提升,代码重复量很高,对象满天飞
  • 为了避免这种情况,Spring使用工厂模式编程,写一个工厂,由工厂创建Bean,以后我们如果要对象就直接管工厂要就可以,剩下的事情不归我们管了。Spring IOC容器的工厂中有个静态的Map集合,是为了让工厂符合单例设计模式,即每个对象只生产一次,生产出对象后就存入到Map集合中,保证了实例不会重复影响程序效率。

工厂模式分类

工厂模式分为简单工厂、工厂方法、抽象工厂模式
简单工厂 :用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)

简单工厂模式

简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。

public class Simple_Factory {
     
    public static void main(String[] args){
        Car aodi = CarFactory.createCar("aodi");
        Car bmw = CarFactory.createCar("bmw");
        aodi.run();
        bmw.run();
    }
}

interface Car { //汽车
    void run();
}

class Bmw implements Car {
    @Override
    public void run() {
        System.out.println("this is bmw");
    }
}

class AoDi implements Car {
	public void run() {
		System.out.println("this is aodi");
	}
}

class CarFactory {

	 public static Car createCar(String name) {
		if(name.equalsIgnoreCase("aodi")){
			return new AoDi();
		}
		if(name.equalsIgnoreCase("bmw")){
			return new Bmw();
		}
		return null;
	}
}
  • 优点/缺点
    优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
    缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则

工厂方法模式

工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

public class Factory_Method {
    public static void main(String[] args) {
        Car aodi = new AoDiFactory().createCar();
        Car bmw = new BmwFactory().createCar();
        aodi.run();
        bmw.run();
    }
}
//创建工厂 
interface Car {
    void run();
}

//创建工厂方法调用接口(所有的产品需要new出来必须继承他来实现方法) 
interface CarFactory {
    Car createCar();
}

//创建工厂的产品
class AoDi implements Car {
    @Override
    public void run() {
        System.out.println("this is aodi");
    }
}
class Bmw implements Car {
    @Override
    public void run() {
        System.out.println("this is bmw");
    }
}

//创建工厂方法调用接口的实例
class AoDiFactory implements CarFactory {    
    public Car createCar() {        
        return new AoDi();
    }
}
class BmwFactory implements CarFactory {
    public Car createCar() {
        return new Bmw();
    }
}

抽象工厂模式

抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。

1.创建第一个子工厂
//汽车
interface Car {
    void run();
}

class CarA implements Car{
    public void run() {
        System.out.println("宝马");
    }
}
class CarB implements Car{
    public void run() {
        System.out.println("摩拜");
    }
}
2.创建第二个子工厂
//发动机
interface Engine {
    void run();
}
class EngineA implements Engine {
    public void run() {
        System.out.println("转的快!");
    }
}

class EngineB implements Engine {
    public void run() {
        System.out.println("转的慢!");
    }
}
//3.创建一个总工厂
interface TotalFactory {
    // 创建汽车
    Car createChair();
    // 创建发动机
    Engine createEngine();
}
//总工厂实现类,由他决定调用哪个工厂的那个实例
class TotalFactoryReally implements TotalFactory {
    public Engine createEngine() {
        return new EngineA();
    }
    public Car createChair() {
        return new CarA();
    }
}
4.测试
public class Abstract_Factory {
    public static void main(String[] args) {
        TotalFactory totalFactory2 = new TotalFactoryReally();
        Car car = totalFactory2.createChair();
        car.run();
        TotalFactory totalFactory = new TotalFactoryReally();
        Engine engine = totalFactory.createEngine();
        engine.run();
    }
}

建造者模式

将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
个人总结:将一些不会变的基本组件,通过builder,组合,构建复杂对象,实现分离

public class BuilderDemo {
 
    public static void main(String[] args) {
        PriceBuilder priceBuilder = new PriceBuilder();
        System.out.println("Car1和Car2:"+priceBuilder.Car1AndCar2());
        System.out.println("Car1和Bus:"+priceBuilder.Car1AndBus());
    }
}
//基本组件
interface Car {
}
//基本组件1
class Car1 implements Car{
    int price = 20;
}
//基本组件2
class Car2 implements Car{
    int price = 90;
}
//基本组件3
class Bus {
    int price = 500;
}
class PriceBuilder {
    //car1和car2的总价格
    public int Car1AndCar2() {
        int priceOfCar1 = new Car1().price;
        int priceOfCar2 = new Car2().price;
        return priceOfCar1+priceOfCar2;
    }
    //car1和bus的总价格
    public int Car1AndBus() {
        int priceOfCar1 = new Car1().price;
        int priceOfBus = new Bus().price;
        return priceOfCar1+priceOfBus;
    }
}

原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
个人总结:将对象复制了一份并返还给调用者,对象需继承Cloneable并重写clone()方法

public class Prototype implements Cloneable{
 
    private String message = "hello";
     
    public Object clone() throws CloneNotSupportedException{
        Prototype proto = (Prototype) super.clone();
        //操作克隆对象
        proto.message += " world!";
        return proto; 
    }
     
    public static void main(String[] args) throws CloneNotSupportedException {
        Prototype p = (Prototype)new Prototype().clone();
        //操作克隆对象
        System.out.println(p.message);
    }
}

结构型模式(共七种)

适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
个人总结:衔接两个不兼容、独立的接口的功能,使得它们能够一起工作。适配器起中介作用。

public class Adapter {
 
    public static void main(String[] args) {
        //兼容了高级功能的普通播放器
        Player player = new Player();
        player.play();
    }
}
//普通的播放器
interface MediaPlayer {
    public void play();
}
//高级的播放器
interface AdvanceMediaPlayer {
    public void playVideo();
}
//视频播放器(高级的播放器)
class VideoPlayer implements AdvanceMediaPlayer {
    @Override
    public void playVideo(){
        System.out.println("play video!");
    }
}
//适配器(衔接了普通播放器与高级播放器这两个独立接口的功能)
class MediaAdapter implements MediaPlayer {
    AdvanceMediaPlayer advanceMediaPlayer;
    public MediaAdapter() {
        advanceMediaPlayer = new VideoPlayer();
    }
    @Override
    public void play() {
        advanceMediaPlayer.playVideo();
    }
}
//普通播放器
class Player implements MediaPlayer {
    //兼容高级播放器的适配器
    MediaAdapter mediaAdapter = new MediaAdapter();
 
    @Override
    public void play() {
        mediaAdapter.play();
    }
}

装饰器模式

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
个人总结:创建类的装饰类,对被装饰类增强功能。装饰模式是继承的一个替代模式。

public class Decorator {
 
    public static void main(String[] args) {
        Animals dog = new AnimalsDecorator(new Dog());
        dog.run();
    }
}
interface Animals {
    public void run();
}
//被装饰类
class Dog implements Animals{
    @Override
    public void run() {
        System.out.println("dog run!");
    }
}
//装饰类
class AnimalsDecorator implements Animals {
    private Animals animals;
    //动态装饰,参数为Animals接口,传入什么实现就装饰什么实现
    //继承不能做到这一点,继承的功能是静态的,不能动态增删。
    public AnimalsDecorator(Animals animals) {
        this.animals = animals;
    }
    @Override
    //装饰run()方法
    public void run() {
        animals.run();
        System.out.println("fast!");
    }
}

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

个人总结:创建类的代理类,间接访问被代理类的过程中对其功能加以控制,例如在某个函数执行前后添加额外功能。(代理例子:买火车票不一定在火车站买,也可以去代售点)。和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制,"形式"虽然相似,"语义"却截然不同"。起中介作用

public class Proxy {
 
    public static void main(String[] args) {
        Animals dog = new DogProxy(new Dog());
        dog.run();
    }
}
interface Animals {  
    public void run();  
}  
class Dog implements Animals {  
    @Override  
    public void run() {  
        System.out.println("run!");  
    }  
}  
//通过代理类,在被代理类的run()方法执行前后添加额外的功能
class DogProxy implements Animals {  
    private Animals animals;  
    public DogProxy(Animals animals){  
        super();  
        this.animals = animals;
    }  
    @Override  
    public void run() {  
        before();  
        animals.run();  
        atfer();  
    }  
    private void atfer() {  
        System.out.println("after run!");  
    }  
    private void before() {  
        System.out.println("before run!");  
    }  
}

外观模式

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
个人总结:在客户端和复杂系统之间再加一层,在这一层中将调用顺序、依赖关系等处理好。提供一个容易使用的外观层。

public class Facade {
 
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.put();
    }
}
class CPU {
    public void work(){
        //复杂的操作
        System.out.println("CPU is working!");
    }
}
class Disk {
    public void put(){
        //复杂的操作
        System.out.println("put in disk!");
    }
}
//外观类,隐藏了系统的复杂性,提供简化的方法(访问系统的接口)
//客户端不需要知道系统内部的复杂联系
class Computer {
    private CPU cpu;
    private Disk disk;
     
    public Computer(){
        cpu = new CPU();
        disk = new Disk();
    }
    public void work(){
        cpu.work();
    }
    public void put(){
        disk.put();
    }
}

桥接模式

将抽象部分与实现部分分离,使它们都可以独立的变化。
个人总结:通过对Bridge类的调用,实现了对同一接口下不同实现类的调用;建立一个继承于同一抽象的不同实现类之间的关联关系,这个关系由Bridge类桥接起来。

public class Bridge {
 
    public static void main(String[] args) {
        AnimalsBridge bridge = new AnimalsBridge(new Dog());
        bridge.method();
    }
}
//接口
interface Animals {  
    public void method();  
}
//实现1
class Cat implements Animals {  
    @Override  
    public void method() {  
        System.out.println("this is cat!");  
    }  
} 
//实现2
class Dog implements Animals {  
    @Override  
    public void method() {  
        System.out.println("this is dog!");  
    }  
}  
//将Animals接口下的不同实现,
//通过桥接模式使它们在抽象层建立一个关联关系。
//实现之间独立变化,减少耦合
class AnimalsBridge {
    private Animals animals;
    public AnimalsBridge(Animals animals) {
        this.animals = animals;
    }
    public void method(){
        animals.method();
    }
}

组合模式

将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
个人总结:创建了一个包含自己对象组的类,并提供修改对象组的方法。应用场景,如树形菜单,文件、文件夹的管理。

public class Composite {
 
    public static void main(String[] args) {
        Person person = new Person("小明");
        person.addFriends(new Person("小红"));
        person.addFriends(new Person("小白"));
        System.out.println(person.getFriends());
    }
}
class Person {
    private String name;
    //包含自己的对象组
    private List<Person> friends = new ArrayList<Person>();
     
    public Person(String name){
        this.name = name;
    }
    public Person addFriends(Person p){
        friends.add(p);
        return this;
    }
    public String getName(){
        return this.name;
    }
    public List<Person> getFriends(){
        return this.friends;
    }
    public String toString(){
        return this.name;
    }
}

享元模式

运用共享技术有效地支持大量细粒度的对象。
个人总结:重用现有的同类对象,若未找到匹配的对象,则创建新对象。例如,数据库的连接池。减少对象的创建,降低系统内存,提高效率。

public class Flyweight {
 
    public static void main(String[] args) {
        //red Circle默认存在,所以拿的时候不用new
        Circle circle = CircleFactory.getCircle("red");
        circle.draw();
        for(int i=0;i<2;i++) {
            //第一次拿的时候需要new green Circle,第二次拿的时候不用new
            circle = CircleFactory.getCircle("green");
            circle.draw();
        }
    }
}
class Circle {
    private String color;
    public Circle(String color){
        this.color = color;
    }
    public void draw(){
        System.out.println(color+" Circle!");
    }
}
class CircleFactory {
    private static final HashMap<String, Circle> circleMap = new HashMap<String, Circle>();
    static {
        //初始化,存放red Circle
        circleMap.put("red", new Circle("red"));
    }
    public static Circle getCircle(String color) {
        Circle circle = (Circle)circleMap.get(color);
        //Map如果不存在该颜色的Circle,则新建
        if(circle == null) {
            circle = new Circle(color);
            circleMap.put(color, circle);
            System.out.println("new a circle of color: "+color);
        }
        //如果存在,则返回Map中的对象
        return circle;
    }
}

行为型模式(共十一种)

策略模式

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
个人总结:统一接口下的一系列算法类(多种策略),用一个类将其封装起来,使它们(多种策略)可动态切换。和工厂模式的区别:工厂模式是创建型模式,是为了创建不同对象;而策略模式是行为模式,为了选择不同的行为。

public class Strategy {
 
    public static void main(String[] args) {
        OperationStrategy operationStrategy = new OperationStrategy(new OperationAdd());
        operationStrategy.executeStrategy(15, 21);
    }
}
interface Operation{
    public void doOperation(int a, int b);
}
//策略1
class OperationAdd implements Operation{
    public void doOperation(int a, int b){
        System.out.println(a+"+"+b+"="+(a+b));
    }
}
//策略2
class OperationMultiply implements Operation{
    public void doOperation(int a, int b){
        System.out.println(a+"*"+b+"="+(a*b));
    }
}
//封装一系列策略,可任意替换策略(实现同一个接口)
class OperationStrategy{
    private Operation operation;
    public OperationStrategy(Operation operation){
        this.operation = operation;
    }
    //执行策略
    public void executeStrategy(int a, int b){
        operation.doOperation(a, b);
    }
}

模板方法模式

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
个人总结:将一些固定步骤、固定逻辑的方法封装成模板方法。调用模板方法即可完成那些特定的步骤。例如,spring中对Hibernate的事务管理,开启session、关闭session等固定步骤不需重复写,直接丢给一个实体保存。

public class Template {
 
    public static void main(String[] args) {
        Game game = new FootballGame();
        game.play();
    }
}
abstract class Game {
    //步骤1,初始化游戏
    abstract void initialize();
    //步骤2,开始游戏
    abstract void startPlay();
    //,步骤3,结束游戏
    abstract void endPlay();
    //主方法,模板方法,设置为final,在抽象类中实现
    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }
}
class FootballGame extends Game {
    @Override
    void initialize() {
        System.out.println("Football Game Initialized! Start playing.");
    }
    @Override
    void startPlay() {
        System.out.println("Football Game Started. Enjoy the game!");
    }
    @Override
    void endPlay() {
        System.out.println("Football Game Finished!");
    }
}

观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
个人总结:一个对象(被观察者)状态变化时,通知所有依赖于它的对象(观察者);这种依赖方式具有双向性:观察者指定被观察的对象,或者被观察对象添加观察者,下面例子采用后者方式

public class Observer {
 
    public static void main(String[] args) {
        Subject subject = new Subject();
        subject.addSubjectObserver(new Observer1());
        subject.addSubjectObserver(new Observer2());
        subject.setState(1);
    }
}
class Subject {
    //一对多关系,多个该类的观察者
    private List<SubjectObserver> subjectObservers = new ArrayList<SubjectObserver>();
    //状态(被观察),发生变化时通知所有观察者
    private int state;
    public void setState(int state) {
        this.state = state;
        //改变状态,通知所有观察者
        notifyAllSubjectObservers();
    }
    public void addSubjectObserver(SubjectObserver subjectObserver) {
        subjectObservers.add(subjectObserver);
    }
    //通知所有观察者
    public void notifyAllSubjectObservers() {
        for (SubjectObserver subjectObserver : subjectObservers) {
            subjectObserver.alert();
        }
    }
}
abstract class SubjectObserver {
    protected Subject subject;
    public abstract void alert();
}
//观察者1
class Observer1 extends SubjectObserver {
    @Override
    public void alert() {
        System.out.println("Observer1: subject is changed!");
    }
}
//观察者2
class Observer2 extends SubjectObserver {
    @Override
    public void alert() {
        System.out.println("Observer2: subject is changed!");
    }
}

迭代器模式

提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
个人总结:Java中的iterator的简单实现原理。将聚合类中遍历各个元素的行为分离出来,封装成迭代器,让迭代器来处理遍历的任务;使简化聚合类,同时又不暴露聚合类的内部。

public class IteratorDemo {

    public static void main(String[] args) {
        MyContainer myContainer = new MyContainer();
        Iterator iterator = myContainer.getIterator();
        while(iterator.hashNext())
            System.out.println(iterator.next());
    }
}
//迭代器接口
interface Iterator {
    public boolean hashNext();
    public Object next();
}
//容器接口
interface Container {
    public Iterator getIterator();
}
//自定义容器(聚合类)
class MyContainer implements Container{
    public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
    @Override
    public Iterator getIterator(){
        return new MyIterator();
    }
    //自定义迭代器,迭代器类定义为容器类的内部类
    private class MyIterator implements Iterator{
        int index = 0;
        //自定义遍历规则
        @Override
        public boolean hashNext(){
            if(index < names.length)
                return true;
            return false;
        }
        @Override
        public Object next(){
            if(this.hashNext())
                return names[index++];
            return null;
        }
    }
}

责任链模式

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
个人总结:在Handler类里面聚合自己,形成一条Handler链(或树、环等),并且可以将请求往下一个Handler传递(只允许传给另一个,而不允许传给多个)。例子:Struts拦截器,Filter过滤器

public class Chain_of_Responsibility {

    public static void main(String[] args) {
        ResponsibilityHandler handler1 = new ResponsibilityHandler("handler1");
        ResponsibilityHandler handler2 = new ResponsibilityHandler("handler2");
        ResponsibilityHandler handler3 = new ResponsibilityHandler("handler3");
        handler1.setResponsibilityHandler(handler2);
        handler2.setResponsibilityHandler(handler3);
        handler1.operator();//操作请求会沿着这条链传递下去,
    }
}
//责任处理器/接收器
class ResponsibilityHandler {
    //聚合自己,构成一条责任链
    private ResponsibilityHandler responsibilityHandler = null;
    private String name;
    public ResponsibilityHandler(String name){
        this.name = name;
    }
    public ResponsibilityHandler next(){
        return this.responsibilityHandler;
    }
    public void setResponsibilityHandler(ResponsibilityHandler responsibilityHandler){
        this.responsibilityHandler = responsibilityHandler;
    }
    public void operator(){
        System.out.println(name+" is handler!");
        if(this.next() != null){
            //将请求发送到下一个责任接收器
            next().operator();
        }
    }
}

命令模式

将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
个人总结:三种角色(调用者→接受者→命令);解耦行为请求者和行为实现着,实现请求和执行分开;调用者选择命令发布,命令指定执行者。

public class CommandDemo {
 
    public static void main(String[] args) {
        Receiver receiver = new Receiver("小明");
        //指定命令的执行者
        Command shootCommand = new ShootCommand(receiver);
        Command otherCOmmand = new OtherCommand(receiver);
        Invoker invoker = new Invoker();
        invoker.addCommands(shootCommand);
        invoker.addCommands(otherCOmmand);
        invoker.sendCommands();
    }
}
//命令
interface Command {
    public void execute();
}
//射击命令
class ShootCommand implements Command{
    private Receiver receiver;
    public ShootCommand(Receiver receiver){
        this.receiver = receiver;
    }
    public void execute(){
        System.out.println("shootCommand is execute:");
        receiver.action();
    }
}
//其他命令
class OtherCommand  implements Command{
    private Receiver receiver;
    public OtherCommand(Receiver receiver){
        this.receiver = receiver;
    }
    public void execute(){
        System.out.println("otherCommand is execute:");
        receiver.action();
    }
}
//命令接受者(士兵)
class Receiver {
    public String name;
    public Receiver(String name){
        this.name = name;
    }
    //行动,执行命令
    public void action(){
        System.out.println(name+" received the command!");
    }
}
//命令调用者(司令官)
class Invoker {
    private List<Command> commandList = new ArrayList<Command>();
    public void addCommands(Command command){
        this.commandList.add(command);
    }
    //发出命令
    public void sendCommands(){
        for(Command command : commandList){
            command.execute();
            System.out.println();
        }
        commandList.clear();
    }
}

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
个人总结:创建一个备忘录类,用来存储原始类的信息;同时创建备忘录仓库类,用来存储备忘录类,当然,原始类与备忘录类的对应关系要处理好。

public class MementoDemo {

    public static void main(String[] args) {
        //待备份的类
        Originator originator = new Originator();
        originator.setState("123");
        System.out.println("初始化的状态为:"+originator.getState());
        MementoStorage mementoStorage = new MementoStorage();
        mementoStorage.add(originator.createMemento());
        originator.setState("321");
        System.out.println("修改后的状态为:"+originator.getState());
        originator.restoreMemento(mementoStorage.get(0));
        System.out.println("还原后的状态为:"+originator.getState());
    }
}
//备忘录类
class Memento {
    private String state;
    public Memento(String state){
        this.state = state;
    }
    public String getState(){
        return this.state;
    }
}
//备忘录类仓库,备忘录管理类
class MementoStorage {
    private List<Memento> mementoList = new ArrayList<Memento>();
    public void add(Memento state){
        mementoList.add(state);
    }
    public Memento get(int index){
        return mementoList.get(index);
    }
}
//原始类
class Originator {
    private String state;
    public void setState(String state){
        this.state = state;
    }
    public String getState(){
        return this.state;
    }
    //创建备份
    public Memento createMemento(){
        //把需要备份的信息全部存储到备份类中。
        return new Memento(state);
    }
    //还原备份
    public void restoreMemento(Memento memento){
        //把备份类中存储的信息还原
        state = memento.getState();
    }
}

状态模式

允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
个人总结:对象具有多种状态,且每种状态具有特定的行为;应用场景: 行为随状态改变而改变的场景。代码形式似乎也和哪种设计模式相似,还是那句话,设计模式提倡的是思想,而不是形式。

public class StateDemo {
 
    public static void main(String[] args) {
        QQContext context = new QQContext();
        //设置状态,不同的状态对应不同的行为
        context.setState(new OnlineState());
        context.getState().getMessage();
    }
}
interface State{
    public void getMessage();
}
//在线状态(状态对象)
class OnlineState implements State{
    //在线状态下的行为
    public void getMessage(){
        System.out.println("在线中,对好友可见!");
    }
}
//隐身状态(状态对象)
class StealthState implements State{
    //隐身状态下的行为
    public void getMessage(){
        System.out.println("隐身中,对好友不可见!");
    }
}
//QQ的登陆状态类
class QQContext {
    private State state;
 
    public State getState() {
        return state;
    }
    public void setState(State state) {
        this.state = state;
    }
}

访问者模式

主要将数据结构与数据操作分离。
个人总结:在被访问的类里面加一个对外提供接待访问者的接口(如下面例子的accept()方法)。访问者封装了对被访问者结构的一些杂乱操作,避免这些操作"污染"被访问者,解耦结构与算法,同时具有优秀的扩展性。

public class Visitor {
 
    public static void main(String[] args) {
        Computer computer = new Computer("myComputer");
        //computer接受computerVisitor的访问
        computer.accept(new ComputerVisitor());
    }
}
//被访问者
class Computer {
    private String computerName;
    public String getComputerName(){
        return computerName;
    }
    public Computer(String computerName){
        this.computerName = computerName;
    }
    //提供接待访问者的接口
    public void accept(ComputerVisitor computerVisitor){
        //访问者访问自身
        computerVisitor.visit(this);
    }
}
//访问者
class ComputerVisitor {
    //访问Computer类,将被访问者的引用传入访问者
    public void visit(Computer computer){
        System.out.println("访问"+computer+"的name属性:"+computer.getComputerName());
    }
}

中介者模式

用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
个人总结:中介者对象,用来封装关联对象之间的交互操作,使关联对象之间耦合度松散;例如,MVC模式中"控制器"就是"模型"和"视图"的中介者;与适配器模式的区别:适配器模式为了桥接互不兼容的接口,中介者为了分离原始结构和交互行为。

public class Mediator {
 
    public static void main(String[] args) {
        User1 user1 = new User1("小明");
        User2 user2 = new User2("小红");
        UserMediator userMediator = new UserMediator(user1,user2);
        userMediator.introduceYourselves();
    }
}
class User1 {
    private String name;
    public String getName(){
        return name;
    }
    public User1(String name){
        this.name = name;
    }
}
class User2 {
    private String name;
    public String getName(){
        return name;
    }
    public User2(String name){
        this.name = name;
    }
}
//中介者,用来封装User1与User2的交互操作
class UserMediator {
    private User1 user1;
    private User2 user2;
    //将User1与User2传入它们的中介者
    public UserMediator(User1 user1, User2 user2){
        this.user1 = user1; this.user2 = user2;
    }
    public void introduceYourselves(){
        System.out.println("Hello "+user1.getName()+",I'm "+user2.getName());
        System.out.println("Hi "+user2.getName()+",My name is "+user1.getName());
    }
}

解释器模式

给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
个人总结:自己定义一种语言或表达式(附对应的解释器),用来表示一些复杂的频繁发生的行为。例如:正则表达式的解释,sql语句的解释。使用场景极少,会引起效率、性能、维护等问题。

public class Interpreter {
 
    public static void main(String[] args) {
        Context context = new Context();
        //创建自定义变量
        Variable a = new Variable();
        Variable b = new Variable();
        //创建常量 3
        Constant c = new Constant(3);
        //给变量赋值
        context.put(a, 5).put(b, 1);
        //构建语法树(自定义表达式:a+b+3)
        Expression exp = new Add(new Add(a,b), c);
        //解释表达式a+b+3
        int result = exp.interpret(context);
        System.out.println("a+b+3 = "+result);
    }
}
//表达式(所有表达式角色继承该接口,自带解释器)
interface Expression {
    //解释器,解释角色所需参数存储在Context类中
    public int interpret(Context context);
}
//构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。
class Context {
    private Map<Variable,Integer> valueMap = new HashMap<Variable,Integer>();
    public Context put(Variable x, int y){
        valueMap.put(x, y);
        return this;
    }
    public int get(Variable x){
        int value = (Integer) valueMap.get(x);
        return value;
    }
}
//终结符表达式角色--常量
class Constant implements Expression{
    private int a;
    public Constant(int a){
        this.a = a;
    }
    public int interpret(Context context){
        return a;
    }
}
//终结符表达式角色--变量
class Variable implements Expression{
    public int interpret(Context context){
        return context.get(this);
    }
}
//非终结符表达式角色--运算符(+)
class Add implements Expression {
    private Expression x, y;
    public Add(Expression x, Expression y){
        this.x = x; this.y = y;
    }
    public int interpret(Context context){
        return x.interpret(context) + y.interpret(context);
    }
}

参考文章

posted @ 2022-04-22 18:03  夏尔_717  阅读(105)  评论(0编辑  收藏  举报