Java设计模式

145 阅读6分钟

设计模式 Design pattern

设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、提高代码可读性、保证代码可靠性。

七大原则

1、开闭原则

一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。

2、里氏代换原则

继承必须确保超类所拥有的性质在子类中仍然成立。 通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能

3、依赖倒置原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。 其核心思想是:要面向接口编程,不要面向实现编程。

4、单一职责原则

职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。 单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。

5、接口隔离原则

要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

6、最少知道原则(迪米特法则)

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。 其目的是降低类之间的耦合度,提高模块的相对独立性

从迪米特法则的定义和特点可知,它强调以下两点:

  1. 从依赖者的角度来说,只依赖应该依赖的对象。
  2. 从被依赖者的角度说,只暴露应该暴露的方法。

7、合成复用原则

在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

总结

设计原则一句话归纳目的
开闭原则对扩展开放,对修改关闭降低维护带来的新风险
依赖倒置原则高层不应该依赖低层,要面向接口编程更利于代码结构的升级扩展
单一职责原则一个类只干一件事,实现类要单一便于理解,提高代码的可读性
接口隔离原则一个接口只干一件事,接口要精简单一功能解耦,高聚合、低耦合
迪米特法则不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度只和朋友交流,不和陌生人说话,减少代码臃肿
里氏替换原则不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义防止继承泛滥
合成复用原则尽量使用组合或者聚合关系实现代码复用,少使用继承降低代码耦合
目的只有一个:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。

三大分类

  • 创建型模式: 对象实例化的模式,创建型模式用于解耦对象的实例化过程
  • 结构型模式: 把类和对象结合在一起形成一个更大的结构
  • 行为型模式: 类的对象如何交互,及划分责任和算法 image.png

常用模式

工厂方法模式、抽象工厂模式、单例模式、代理模式

工厂方法模式

工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。 image.png

抽象工厂模式

所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么 image.png

单例模式

指一个类只有一个实例,且该类能自行创建这个实例的一种模式 懒汉式单例:

public class LazySingleton {
  // 证 instance 在所有线程中同步
  private static volatile LazySingleton instance = null; 
  
  private LazySingleton() {
  } // rivate 避免类在外部被实例化

  public static synchronized LazySingleton getInstance() {
      //getInstance 方法前加同步
      if (instance == null) {
          instance = new LazySingleton();
      }
      return instance;
  }
}

饿汉式单例:

public class HungrySingleton {
  rivate static final HungrySingleton instance = new HungrySingleton();

  private HungrySingleton() {
  }

  public static HungrySingleton getInstance() {
  return instance;
  }
}

静态内部类:

public class SignleObject4 {
    private static class SignletonHolder {
        public static SignleObject4 instance = new SignleObject4();
    }
 
    private SignleObject4()  { }
 
    public static SignleObject4 getInstance() {
        return SignletonHolder.instance;
    }
}

枚举:

class Resource{
}
public enum SomeThing {
    INSTANCE;
    private Resource instance;
    private SomeThing() {
        instance = new Resource();
    }
    public Resource getInstance() {
        return instance;
    }
}

代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。 image.png 动态代理示例:

// 辅导班类 JdkFuDao
public class JdkFuDao 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(), this);
    }

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

    private void after() {
        System.out.println("双方同意,开始辅导");
    }

    private void before() {
        System.out.println("这里是C语言中文网辅导班,已经收集到您的需求,开始挑选老师");
    }
}
// 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
public class ZhaoLiu implements IPerson {
    @Override
    public void findTeacher() {
        System.out.println("符合赵六的要求");
    }
}
// 执行
public class Test {
    public static void main(String[] args) {
        JdkFuDao jdkFuDao = new JdkFuDao();

        IPerson zhaoliu = jdkFuDao.getInstance(new ZhaoLiu());
        zhaoliu.findTeacher();
    }
}

应用场景

模式应用场景
单例模式希望在系统中某个类的对象只能存在一个网站计数器、数据库连接池
工厂模式在编码时不能预见需要创建哪种类的实例发送邮件和短信(共同特征:发送信息)
代理模式无法或不想直接引用某个对象或访问某个对象存在困难时AOP、日志、Spring事务、全局捕获异常

参考资料:

c.biancheng.net/view/1351.h…

www.cnblogs.com/pony1223/p/…