设计模式目录
一,设计模式概述
●设计模式 是前辈们对代码开发经验的总结, 是解决特定问题的一系列套路, 他不是语法规定, 而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
学习设计模式的意义
●设计模式的本质是面向对象设计原则的实际运用, 是对类的封装性, 继承性和多态性
以及类的关联关系和组合关系的充分理解
●正确使用设计模式具有以下优点::
可以提高程序员的思维能力, 编程能力和设计能力
使程序设计更加标准化, 代码编制更加工程化, 使软件开发效率大大提高, 从而缩短软件的开发周期
使设计的代码可重用性高, 可读性强, 可靠性高, 灵活性好, 可维护性强
●设计模式的的基本要素:模式名称、问题、解决方案、效果
GoF 23
●Gof23
一种思维, 一种态度, 一种进步
●创建型模式:
单例模式, 工厂模式, 抽象工厂模式, 建造者模式, 原型模式
●结构型模式:
适配器模式, 桥接模式, 装饰模式, 组合模式, 外观模式, 享元模式, 代理模式
●行为型模式:
模板方法模式, 命令模式, 迭代器模式, 观察者模式, 中介者模式, 备忘录模式, 解释器模式, 状态模式, 策略模式, 职责链模式, 访问者模式
OOP七大原则
开闭原则:对扩展开放,对修改关闭(当需求需要改变的时候,尽量去扩展)
里氏替换原则: 继承必须确保父类所拥有的性质在子类中仍然成立(尽量不重写父类的方法)
依赖倒置原则: 要面向接口编程,不要面向实现编程
单一职责原则: 控制类的粒度大小,将对象解耦,提高其内聚性(一个对象不应该担任太多的职责,原子性,单一的方法做单一的事情)
接口隔离原则: 要为各个类建立他们需要的专用接口
迪米特法则: 只与你的直接朋友交谈,不跟“陌生人”说话,降低代码之间的耦合度
合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
狗是(is a)动物 继承
人由脑袋等各部分组成(has a)
二,单例模式
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,
然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
饿汉式、DCL懒汉式
单例是java的常用23种设计模式之一
它其中又有饿汉式和懒汉式等几种模式
区别就在初始化类对象的时机:
饿汉:饿了,就要马上吃,所以得先初始化new出对象准备好,直接取即可
懒汉:懒了,用到了才会初始化返回
单例的实现主要是通过以下两个步骤:
1,将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,
只有通过该类提供的静态方法来得到该类的唯一实例;
2,在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,
如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
(1)饿汉式
不管程序是否需要这个对象的实例,总是在类加载的时候就先创建好实例,
理解起来就像不管一个人想不想吃东西都把吃的先买好,如同饿怕了一样。
//恶汉式单例
public class Hungry {
//可能会浪费空间
private byte[] data1=new byte[1024*1024];
private byte[] data2=new byte[1024*1024];
private byte[] data3=new byte[1024*1024];
private byte[] data4=new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY=new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
(2)DCL懒汉式
如果一个对象使用频率不高,占用内存还特别大,明显就不合适用饿汉式了,这时就需要一种懒加载的思想,
当程序需要这个实例的时候才去创建对象,就如同一个人懒的饿到不行了才去吃东西。
懒汉式:
如果在多线程下,一个线程进入了if (lazy== null)判断语句块,还未来得及往下执行,另一个线程也通过了这个
判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
//懒汉式例子
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName()+"--->OK");
}
private static Lazy lazy;
//单线程下确实单例OK
public static Lazy getInstance(){
if(lazy==null){
lazy=new Lazy();
}
return lazy;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
lazy.getInstance();
}).start();
}
}
}
双重加锁机制<DCL懒汉式>
使用双重检测同步延迟加载去创建单例的做法是一个非常优秀的做法,其不但保证了单例
,而且切实提高了程序运行效率
优点:线程安全;延迟加载;效率较高。
Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (lazy== null)检查,
这样就可以保证线程安全了。这样,实例化代码只用执行一次,
后面再次访问时,判断if (lazy== null),直接return实例化对象。
public class Lazy {
private Lazy(){
System.out.println(Thread.currentThread().getName()+"--->OK");
}
//避免指令重排
private volatile static Lazy lazy;
//双重检测锁模式--DCL懒汉式
public static Lazy getInstance(){
if(lazy==null){
//加了锁保证只有一个
synchronized (Lazy.class){
if(lazy==null){
lazy=new Lazy();//不是一个原子性操作
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个空间
*
* 就有可能出现指令重排问题
* 比如执行的顺序是1 3 2 A线程
* B线程//此时的lazy还没有完成构造
* 我们就可以添加volatile避免指令重排问题
*/
}
}
}
return lazy;
}
//多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
lazy.getInstance();
}).start();
}
}
}
(3)静态内部类
//静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER=new Holder();
}
}
单例不安全, 因为反射
private Lazy(){
System.out.println(Thread.currentThread().getName()+"--->OK");
}
//反射
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//反射可破坏单例模式
Lazy instance=Lazy.getInstance();
//通过构造器创建对象
Constructor<Lazy> lazyConstructor=Lazy.class.getDeclaredConstructor(null);
lazyConstructor.setAccessible(true);//取消安全检测
Lazy instance2=lazyConstructor.newInstance();//创建类的对象,本质上是调用了类的无参构造器
System.out.println("instance-->"+instance.hashCode());
System.out.println("instance2-->"+instance2.hashCode());
}
解决:
private Lazy() {
synchronized(Lazy.class){
if(lazy!=null){
throw new RuntimeException("异常:不要试图使用反射破坏");
}
}
System.out.println(Thread.currentThread().getName()+"--->OK");
}
问题:两个对象都用反射破坏
//反射
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
//反射可破坏单例模式
//Lazy instance=Lazy.getInstance();
//通过构造器创建对象
Constructor<Lazy> lazyConstructor=Lazy.class.getDeclaredConstructor(null);
lazyConstructor.setAccessible(true);//取消安全检测
Lazy instance2=lazyConstructor.newInstance();//创建类的对象,本质上是调用了类的无参构造器
Lazy instance=lazyConstructor.newInstance();
System.out.println("instance-->"+instance.hashCode());
System.out.println("instance2-->"+instance2.hashCode());
}
解决:红绿灯
private static boolean flag=false;
private Lazy() {
synchronized(Lazy.class){
if(flag==false){
flag=true;
}else{
throw new RuntimeException("异常:不要试图使用反射破坏");
}
}
System.out.println(Thread.currentThread().getName()+"--->OK");
}
问题:
源码 枚举自带单例模式
(4)枚举
//enum本身就是一个Class 类
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) {
EnumSingleton instance1=EnumSingleton.INSTANCE;
EnumSingleton instance2=EnumSingleton.INSTANCE;
System.out.println("instance1--->"+instance1.hashCode());
System.out.println("instance2--->"+instance2.hashCode());
}
}
问题:
class Test{
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingleton instance1=EnumSingleton.INSTANCE;
Constructor<EnumSingleton> enumSingletonConstructor=EnumSingleton.class.
getDeclaredConstructor(null);
enumSingletonConstructor.setAccessible(true);
EnumSingleton instance2=enumSingletonConstructor.newInstance();
System.out.println("instance1--->"+instance1.hashCode());
System.out.println("instance2--->"+instance2.hashCode());
}
}
正常报错:
分析:
解决:
反编译源码:
package com.wangsen.demo1.Singleton;
public final class EnumSingleton extends Enum
{
public static final EnumSingleton INSTANCE;
private static final EnumSingleton $VALUES[];
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(com/wangsen/demo1/Singleton/EnumSingleton, name);
}
private EnumSingleton(String s, int i)
{
super(s, i);
}
public EnumSingleton getInstance()
{
return INSTANCE;
}
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
}
Constructor<EnumSingleton> enumSingletonConstructor=EnumSingleton.class.
getDeclaredConstructor(String.class,int.class);
三,工厂模式
概述
作用:
●实现了创建者和调用者的分离
●详细分类
简单工厂模式
工厂方法模式
抽象工厂模式
OOP七大原则
●开闭原则: 一个软件的实体应当对扩展开放, 对修改关闭
●依赖倒转原则: 要针对接口编程, 不要针对实现编程
●迪米特法则: 只与你直接的朋友通信, 而避免和陌生人通信
核心本质
●实例化对象不使用new, 用工厂方法代替
●将选择实现类, 创建对象统一管理和控制. 从而将调用者跟我们的实现类解耦
三种模式:
●简单工厂模式
用来生产统一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
●工厂方法模式
用来生产同一等级结构中的固定产品(支持增加任意产品)
●抽象工厂模式
围绕一个超级工厂创建其他工厂, 该超级工厂又称为其他工厂的工厂
简单工厂模式(静态工厂模式)
CarFactory
//开闭原则
public class CarFactory {
public static Car getCar(String car){
if(car.equals("红旗")){
return new RedFlags();
}else if(car.equals("特斯拉")){
return new Tesla();
}else{
return null;
}
}
}
弊端:增加一个新的产品,如果不修改代码,做不到
工厂方法模式 满足开闭原则
抽象工厂模式
●定义︰抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
●适用场景:
○客户端(应用层)不依赖于产品类(手机)实例如何被创建、实现等细节
○强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
○提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
●优点:
具体产品在应用层的代码隔离,无需关心创建的细节
将一个系列的产品统一到一起创建
●缺点:
规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
增加了系统的抽象性和理解难度
代码:
产品
IPhoneProduct
//手机产品接口
public interface IPhoneProduct {
void start();
void shutdown();
void call();
void sendMsg();
}
IRouterProduct
//路由器产品接口
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
HuaweiPhone
//华为手机
public class HuaweiPhone implements IPhoneProduct {
@Override
public void start() {
System.out.println("华为开机");
}
@Override
public void shutdown() {
System.out.println("华为关机");
}
@Override
public void call() {
System.out.println("华为打电话");
}
@Override
public void sendMsg() {
System.out.println("华为发信息");
}
}
HuaweiRouter
//华为路由器
public class HuaweiRouter implements IRouterProduct {
@Override
public void start() {
System.out.println("启动华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void openWifi() {
System.out.println("打开华为Wi-Fi");
}
@Override
public void setting() {
System.out.println("华为设置");
}
}
XiaomiPhone
//小米手机
public class XiaomiPhone implements IPhoneProduct {
@Override
public void start() {
System.out.println("小米开机");
}
@Override
public void shutdown() {
System.out.println("小米关机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
@Override
public void sendMsg() {
System.out.println("小米发信息");
}
}
XiaomiRouter
//小米路由器
public class XiaomiRouter implements IRouterProduct {
@Override
public void start() {
System.out.println("小米开启路由器");
}
@Override
public void shutdown() {
System.out.println("小米关闭路由器");
}
@Override
public void openWifi() {
System.out.println("小米开启Wifi");
}
@Override
public void setting() {
System.out.println("小米设置路由器");
}
}
抽象工厂
IProductFactory
//抽象产品工厂
public interface IProductFactory {
//生产手机
IPhoneProduct iPhoneProduct();
//生产路由器
IRouterProduct iRouterProduct();
}
XiaomiFactory
//小米工厂
public class XiaomiFactory implements IProductFactory {
@Override
public IPhoneProduct iPhoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct iRouterProduct() {
return new XiaomiRouter();
}
}
HuaweiFactory
//华为工厂
public class HuaweiFactory implements IProductFactory{
@Override
public IPhoneProduct iPhoneProduct() {
return new HuaweiPhone();
}
@Override
public IRouterProduct iRouterProduct() {
return new HuaweiRouter();
}
}
类图:
测试:
小结
●简单工厂模式(静态工厂模式)
虽然某种程度上不符合设计原则, 但实际使用最多
●工厂方法模式
不修改已有类的前提下, 通过增加新的工厂类实现扩展
●抽象工厂模式
不可以增加产品, 可以增加产品族
应用场景
●JDK中Calendar的getInstance方法
●JDBC中的Connection对象的获取
●Spring中IOC容器创建管理bean对象
●反射z中Class对象的newstance方法