学设计模式就五部分内容:
- 七大设计原则 、UML图 、23种设计模式(3大类,23种经典模式) 、结合JDK或者Spring看 、自己悟 。
概念较多,看代码移架github : https://github.com/xiaoshaDestiny/My-Note-Utils-Learn/tree/master/learn-design-pattern/src/main/java/com/learn/design
一、七大设计原则
1、单一职责 Single Responsibility
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性。
- 降低变更引起的风险。
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
2、接口隔离原则 Interface Segregation Principle
- 就是拆接口。一个类对另一个类的依赖应该建立在最小的接口上。
3、依赖倒转原则 Dependence Inversion Princple
-
高层模块不应该依赖低层模块,二者都应该依赖其抽象。
-
抽象不应该依赖细节,细节应该依赖抽象。
-
依赖倒转(倒置)的中心思想是面向接口编程。
-
依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。
-
使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
-
低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。 变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化。继承时遵循里氏替换原则。
还不晕的话就看下图,依赖的三种传递方式
4、里氏替换原则 Liskov Substitution Principle
-
继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
-
继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
-
原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替。
5、开闭原则 Open Closed Principle
-
开闭原则是编程中 最基础、最重要的设计原则。
-
一个软件实体如类,模块和函数应该 对扩展开放( 对提供方),对 修改关闭( 对使用方)。用抽象构建框架,用实现扩展细节。
-
当软件需要变化时,尽量 通过扩展软件实体的行为来实现变化,而不是 通过修改已有的代码来实现变化。
-
编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
6、迪米特法则 Demeter Principle
-
一个对象应该对其他对象保持最少的了解。
-
类与类关系越密切,耦合度越大。
-
迪米特法则(Demeter Principle)又叫 最少知道原则,即一个类 对自己依赖的类知道的越少越好。也就是说,对于
被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息。 -
迪米特法则还有个更简单的定义:只与直接的朋友通信。
-
直接的朋友:每个对象都会与其他对象有 耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间
是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现 成员变量, 方法参数, 方法返 回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。 -
迪米特法则的核心是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系。
7、合成复用原则 Composite Reuse Principle
-
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
-
针对接口编程,而不是针对实现编程。
-
为了交互对象之间的 松耦合设计而努力。
二、UML类图
依赖关系是一种比较抽象的关系,而关联关系在Java语言中不能很好体现。
设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验, 模式不是代码,而是 某类问题的通
用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的
一段时间的试验和错误总结出来的。
设计模式的本质提高 软件的维护性,通用性和扩展性,并降低软件的复杂度。
三种类型,共 23 种
创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式。
茴香豆的茴有四种写法。
设计模式的单例模式的8种写法。
/**
* 单例模式的第一种写法: 饿汉式( 静态常量)
* 步骤1:构造器私有,保证外部不能 new
* 步骤2:在类的内部创建对象实例
* 步骤3:提供一个公有的静态方法,返回实例对象
*
* 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。没有多线程问题。
* 缺点:没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
*/
class Singleton{
private Singleton(){
}
private final static Singleton INSTANCE = new Singleton();
public static Singleton getInstance(){
return INSTANCE;
}
}
/**
* 单例模式的第二种写法: 饿汉式(静态代码块)
*
* 这种方式和方式一类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。
* 优:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。没有多线程问题。
* 缺:不用就会造成自资源浪费。
*/
class Singleton{
private Singleton(){
}
private static Singleton INSTANCE;
static {
INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return INSTANCE;
}
}
/**
* 单例模式的第三种写法: 懒汉式(使用到再创建 线程不安全)
*
* 起到了 Lazy Loading 的效果,但是只能在单线程下使用。
* 如果在多线程下,一个线程进入了 if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,
* 这时便会 产生多个实例。所以在多线程环境下不可使用这种方式
*/
class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
/**
* 单例模式的第四种写法: 懒汉式(使用到再创建 线程安全 同步方法)
*
* 解决了线程安全问题每个线程在想获得类的实例时候,执行 getInstance()方法都要进行同步。
* 而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。
* 方法进行同步效率太低
*/
class Singleton{
private static Singleton instance;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
/**
* 单例模式的第五种写法: 懒汉式(使用到再创建 线程安全 同步代码块)
*
* 本意其实是对第四种方式的改进,因为方法4的同步效率太低,所以改为了同步代码块。
* 但是: 这种同步并不能起到线程同步的作用。假如两个线程同时进入了if语句,都有可能会产生多个实例。坚决不能使用这种单例模式。
*/
class Singleton{
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
/**
* 单例模式的第六种写法: 懒汉式(使用到再创建 同步代码块带双重检查)
*
* Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次 if (singleton == null)检查,这样就可以保证线程安全了。
* 这样,实例化代码只用执行一次,后面再次访问时,判断 if (singleton == null),直接 return 实例化对象,也避免的反复进行方法同步.
* 线程安全; 延迟加载; 效率较高,推荐使用。
*/
class Singleton{
/**
* 这个实例变量用volatile关键字修饰,保证线程内存可见
*/
private static volatile Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
/**
* 单例模式的第七种写法: 静态内部类
* 静态内部类的特点:
* 1、当外边的类被装载的时候,内部类是不会被装载的
* 2、静态内部类只会装载一次,在装载的时候,是线程安全的
*
* 原理和特点:
* 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
* 静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,
* 调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。
* 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。避免了线程不安全,利用 静态内部类特点实现延迟加载,效率高,推荐使用。
*
* 总结:
* 1、利用JVM类加载器加载外部类时不加载静态内部类 2、利用JVM装载类去保证线程安全
*/
class Singleton{
private Singleton(){}
/**
* 一个静态内部类,它的一个静态属性Singleton
*/
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
/**
* 单例模式的第八种写法: 枚举
*
* 这借助 JDK1.5 中添加的枚举来实现单例模式。
* 不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
* 这种方式推荐使用。
*/
enum Singleton{
INSTANCE;
public void syaOk(){
System.out.println("ok(这里代表了这个枚举能干的事。。。)");
}
}
** 单例模式 singleton **
一、什么是单例模式
1、所谓类的单例设计模式,就是 采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,
2、并且该类只提供一个取得其对象实例的方法(静态方法)。
比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建 Session 对象。
SessionFactory 并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式。
二、单例模式总工有8种写法:
1、饿汉式 静态常量 (在不确定一定用到的时候会浪费内存,线程安全) final static Singleton INSTANCE = new Singleton();
2、饿汉式 静态代码块 (在不确定一定用到的时候会浪费内存,线程安全) static {INSTANCE = new Singleton();}
3、懒汉式 使用再创建 (线程不安全) getInstance();
4、懒汉式 同步方法 (线程安全了,效率低) synchronized getInstance();
5、懒汉式 同步代码块 (反而线程不安全了) synchronized(){}
6、懒汉式 双重检索 (线程安全) volatile + 双if + 静态代码块
7、静态内部类 1、利用JVM类加载器加载外部类时不加载静态内部类 2、利用JVM装载类去保证线程安全
8、枚举 利用jdk1.5之后枚举类的特性
三、推荐使用的有:
在确保类一定会使用的时候,饿汉式两种可以使用。(1、2)
双重检索、静态内部类、枚举 可以使用。(6、7、8)
四、单例模式的经典使用:
JDK中的 java.lang.Runtime就是单例模式。
使用的是饿汉式写法。
如下:
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() { return currentRuntime; }
private Runtime() {}
五、使用注意细节:
1、单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
2、当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
3、单例模式的使用的场景:需要 频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多
(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如:数据源、session 工厂等)
更多其他设计模式的详细说明:
https://github.com/xiaoshaDestiny/My-Note-Utils-Learn/blob/master/learn-design-pattern/src/main/java/com/learn/design/node