Java设计模式Ⅰ
1. Java设计模式的概述
1.1 设计模式的目的及重要性
(1)软件工程中,设计模式是对软件设计中普遍的存在(反复出现)的各种问题,所提出的解决方案。
(2)代码重用性,可读性,可扩展性,可靠性,是程序呈现高内聚,低耦合的特性等。
1.2 设计模式的7大原则
1.2.1 单一职责原则
(1) 基本介绍:
a. 一个类应该专注于实现一个功能。
(2) 注意事项和原则:
a.即一个类只负责一项职责。
b.提高类的可读性,可维护性
c.降低变更引起的风险
1.2.2 接口隔离原则
(1) 基本介绍:
a.客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立
在最小的接口上
1.2.3 依赖倒置原则
(1)基本介绍:
a.高层模块不应该依赖底层模块,两者都应该依赖其抽象
b.抽象不应该依赖细节,细节应该依赖抽象
c.依赖倒置的中心思想是面向接口编程
d.使用接口或者抽象类的目的是制定好规范,而不涉及任何具体的操作,
把展现的细节交给他们的实现类去完成
(2) 依赖的传递方式:
a.接口传递
b.构造方式传递
c.setter方式传递
(3)依赖倒置的原则及注意事项
a.低级模块尽量都要有抽象类或者接口,或者两者都有,这样程序稳定性更好
b.变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实例化对象间,
就存在一个缓冲区,利于程序扩展和优化
c.继承时遵守里氏替换原则
1.2.4 里氏替换原则
(1) 先说一下继承的思考和说明:
a.继承包含这样一层含义:父类中反是已经实现好的方法,实际上是设定
规范和锲约,虽然它不强制要求所有的子类必须要遵守这些锲约,但是
如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
b.继承再给程序带来遍历的时候,也同样带来了弊端,比如使用继承会
给程序带来侵入性,程序的可移植性降低,增加对象的耦合性,如果
一个类被其他类继承,则当这个类需要修改时,必须考虑到所有的子类,
并且谷类修改后,所有涉及到的子类可能都会产生故障。
(2) 解决继承存在的方法:
a.通用做法就是:原来的父类和子类都继承一个更通俗的基类。原有的
继承关系都去掉,采用依赖,聚合,组合等关系替代。
(3) 基本介绍:
a.所有引用基类的地方必须能透明的使用其子类的对象。
b.在使用继承时,遵守里氏替换原则,在子类中尽量不要重写父类的方法。
c.里氏替换原则告诉我们,继承其实让两个类的耦合性增强了,在适当的
情况下,可以通过聚合,组合,依赖来解决问题。
1.2.5 开闭原则(OCP)
(1) 基本介绍:
1)开闭原则是编程中最基础,最重要的设计原则
2)一个软件实体,如类,模块和函数应该对外扩展开放,对修改关闭,
用抽象构建框架,用实现扩展细节
3)放软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而
不是通过修改已有的代码来实现变化。
4)编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
1.2.6 迪米特法则
(1) 基本介绍:
1)一个对象应该对其他对象保持最少的了解
2)类与类关系越亲密,耦合度越大
3)它又叫最少知道法则,即一个类对自己依赖的类知道的越少越好,
也就是说,对于被依赖的类多复杂,都尽量将逻辑封装在类的内
部,对外除了提供的public方法,不对外泄露任何信息
4)只与直接的朋友通信
5)直接的朋友:每个对象都会与其他对像由耦合度关系,只要两个
对象之间有耦合度关系,我们就说这两个对象是朋友关系。耦
合的方式很多,依赖,关联,组合,聚合等。其中,我们称出
现成员变量,方法参数,方法返回值中的类为直接的朋友。而
出现在局部变量中的类不是直接的朋友,也就是说,陌生的类
最好不要以局部变量的形式出现在类的内部。
(2) 迪米特法则的注意事项和细节:
1)迪米特法则是降低类之间的耦合
2)注意:由于每个类都是减少了不必要的依赖,因此迪米特法则只是要
求降低类之间的耦合关系,并不是要求完全没有依赖
1.2.7 合成复用原则
(1) 基本介绍:
1)原则是尽量使用合成/聚合的方式,而不是继承的方式。
1.3 设计原则核心思想
(1) 找出应用中可能需要变化之处,把他们独立出来。不要和那些不需要变化的代码混在一起。
(2) 针对接口编程,而不是针对实现编程
(3) 为了交互对象之间的松耦合设计而努力
2. 设计模式3的种类型(23种模式)
2.1创建型模式
(1)创建型模式:
单例模式 抽象工厂模式 原型模式 建造者模式 工厂模式。
2.2 结构型模式
(1)结构型模式:
适配器模式 桥接模式 装饰模式 组合模式 外观模式 享元模式 代理模式
2.3 行为型模式
(1)行为型模式:
(3)模板方式模式 命令模式 访问者模式 迭代器模式 观察者模式
中介者模式 备忘录模式 解释器模式 状态模式 策略模式 责任链模式
3. 模式的秘密开始
3.1 单例模式(一共八种)
3.1.1饿汉模式(静态变量):
(1)优缺点:
a.优点:这种写法简单,就是在类装载的时候就完成实例化,避免
了线程同步问题。
b.缺点:在类装载的时候就完成实例化,没有达到lazy loading的
效果,如果从始至终都没有使用过这个实例的话,就造成了不必要的浪费
c.这种方法基于classloder机制避免了多线程的同步问题,不过,
instance在类装载的时候就初始化实例,在单例模式中大多数
都是调用getInstance方法。但是导致类装载的原因有和多种,
因此不能确定有其他方法导致类装载,这时候初始化instance
就没有达到lazy loading的效果
d.结论:这种单例模式可用,但是可能造成内存浪费
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 饿汉式之静态变量 {
public static void main(String[] args) {
// 测试
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); // true
System.out.println(instance.hashCode() == instance1.hashCode()); // true
}
}
class Singleton{
// 1.构造器私有化,外部不能new
private Singleton(){
}
// 2.本类内部创建实例
private final static Singleton singLeton = new Singleton();
// 3.对外提供一个公共的静态方法 返回实例对象
public static Singleton getInstance(){
return singLeton;
}
}
3.1.2饿汉模式(静态代码块):
(1)优缺点:
a.这种方法其实和上面的方法类似,只不过将类实例化的过程交
给了静态代码块去实现,也就是在类装载的时候,就执行静态
代码块中的代码,初始化类的实例。优缺点和上面一样
b.结论:这种单例模式可用,但是也会造成内存的浪费
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 饿汉式之静态代码块 {
public static void main(String[] args) {
Singleton01 singleton01 = Singleton01.getInstance();
Singleton01 s = Singleton01.getInstance();
System.out.println(s == singleton01);
System.out.println(s.hashCode() == singleton01.hashCode());
}
}
class Singleton01{
// 1.构造器私有化,外部不能new
private Singleton01(){
}
// 2.本类内部创建实例
private static Singleton01 singLeTon;
static { // 在静态代码块中, 创建对象
singLeTon = new Singleton01();
}
// 3.对外提供一个公共的静态方法 返回实例对象
public static Singleton01 getInstance(){
return singLeTon;
}
}
3.1.3懒汉式(线程不安全)
(1)优缺点:
a.起到了lazy loading的效果,但是只能在单线程下使用
b.如果在多线程下,一个线程进入了if(singleton == null)判断语句,
还来不及执行,另一个线程也判断通过,这是便会产生多个实例,
所以多线程下不可以使用这种方法
c.结论:在实际开发中,不要使用这种方式
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 懒汉式之线程安全 {
public static void main(String[] args) {
Singleton03 singleton03 = Singleton03.getInstance();
Singleton03 singleton04 = Singleton03.getInstance();
System.out.println(singleton03 == singleton04);
System.out.println(singleton03.hashCode());
System.out.println(singleton04.hashCode());
}
}
class Singleton03{
private static Singleton03 singleton03;
private Singleton03(){
}
// 提供一个静态的共有方法,加入同步处理的代码,解决线程安全问题 即懒汉式
public static synchronized Singleton03 getInstance(){
if (singleton03 == null){
singleton03 = new Singleton03();
}
return singleton03;
}
}
3.1.4 懒汉式(线程安全,同步方法)
(1)优缺点:
a.解决了线程安全问题
b.效率太低了,每个线程在想获得类的实例的时候,执行
getInstance方法都要进行同步,而其实这个方法只执行
一次就够了,后面想获得该实例直接return就行了,方法
进行同步效率是太低了
c.结论:实际开中,不推荐使用
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 懒汉式之线程不安全 {
public static void main(String[] args) {
Singleton02 singleton02 = Singleton02.getInstance();
Singleton02 singleton021 = Singleton02.getInstance();
System.out.println(singleton02 == singleton021);
System.out.println(singleton02.hashCode());
System.out.println(singleton021.hashCode());
}
}
class Singleton02{
private static Singleton02 singleton02;
private Singleton02(){
}
// 提供一个静态的共有方法,当使用到该方法时,才去创建instance 即懒汉式
public static Singleton02 getInstance(){
if (singleton02 == null){
singleton02 = new Singleton02();
}
return singleton02;
}
}
3.1.5 懒汉式(线程安全,同步代码块)
(1)优缺点:
a.跟第三个遇到的情景一样,假如一个线程进入了if(singleton == null)
判断语句,还来不及执行,另一个线程也判断通过,这是便会产生
多个实例,所以多线程下不可以使用这种方法
b.结论:在实际开发中,不要使用。
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 懒汉式之假_线程安全且同步 {
public static void main(String[] args) {
}
}
class Singleton04{
private static Singleton04 singleton04;
private Singleton04(){
}
// 提供一个静态的共有方法, 即懒汉式
public static Singleton04 getInstance(){
if (singleton04 == null){ // 但其实在这里就已经产生线程不安全了
// 加入同步处理的代码,解决线程安全问题
synchronized (Singleton04.class){
singleton04 = new Singleton04();
}
}
return singleton04;
}
}
3.1.6 双重检查
(1)优缺点:
a.Double-check概念是多线程开发中常用到的,就是进行两
次if(singleton == null)检查
b.避免反复进行方法同步
c.线程安全:延迟加载,效率较高
d.结论:实际开发中,推荐使用
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 双重检查 {
public static void main(String[] args) {
Singleton05 singleton05 = Singleton05.getInstance();
Singleton05 singleton06 = Singleton05.getInstance();
System.out.println(singleton05 == singleton06);
System.out.println(singleton05.hashCode());
System.out.println(singleton06.hashCode());
}
}
class Singleton05{
private static volatile Singleton05 singleton05;
private Singleton05(){
}
// 提供一个静态的方法,加入双重检查代码 解决线程安全问题,同时解决懒加载问题
public static synchronized Singleton05 getInstance(){
if (singleton05 == null){
synchronized (Singleton05.class){
if (singleton05 == null){
singleton05 = new Singleton05();
}
}
}
return singleton05;
}
}
3.1.7 静态内部类
(1)优缺点:
a.这种方法采用了类装载的机制保证初始化只有一个线程
b.静态内部类方式在Singleton06类被装载的时候并不会立即实例化,
而是在需要实例化时,调用getInstance方法,才会去装载
SingletonInstance类,从而完成Singleton06实例化
c.类的静态属性只会在第一次加载类的时候初始化,所以我们这里
利用了JVM保证了线程安全,在类进行初始化的时候,别的线
程是进不来的
d.优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
e.结论:推荐使用
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 静态内部类 {
public static void main(String[] args) {
Singleton06 singleton06 = Singleton06.getInstance();
Singleton06 singleton07 = Singleton06.getInstance();
System.out.println(singleton06 == singleton07);
System.out.println(singleton06.hashCode());
System.out.println(singleton07.hashCode());
}
}
class Singleton06{
private Singleton06(){
}
// 静态内部类 提供一个静态属性 Singleton06
private static class SingletonInstance{
private static final Singleton06 SINGLETON_06 = new Singleton06();
}
// 提供一个静态的方法 直接返回SingletonInstance.SINGLETON_06
public static synchronized Singleton06 getInstance(){
return SingletonInstance.SINGLETON_06;
}
}
3.1.8 枚举
(1)优缺点:
a.借用jdk1.5中添加的枚举来实现单例模式,不仅能避免多线程同步
问题,而且还能防止反序列化重新创建新的对象。
b.结论:推荐使用
(2)代码示例:
package com.pattern.设计模式.单例模式;
public class 枚举 {
public static void main(String[] args) {
Singleton07 singleton07 = Singleton07.INSTANCE;
Singleton07 singleton08 = Singleton07.INSTANCE;
System.out.println(singleton07 == singleton08);
System.out.println(singleton07.hashCode());
System.out.println(singleton08.hashCode());
singleton07.shallow();
}
}
enum Singleton07{
INSTANCE;
public void shallow(){
System.out.println("OK!!!!!!");
}
}