常用设计模式

1 篇文章 0 订阅

目录

 

1 设计模式概述

1.1 设计模式的六大原则

1.1.1、开闭原则(Open Close Principle)——扩展性、易于修改

1.1.2、里氏代换原则(Liskov Substitution Principle)——面向对象

1.1.3、依赖倒转原则(Dependence Inversion Principle)——面向接口编程

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

1.1.5、迪米特法则(最少知道原则)(Demeter Principle)

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

1.2 单例模式

1.2.1  为什么使用单例

1.2.2 知识要点

1.3 工厂模式

1.3.1 为什么

1.3.2 知识要点

1.3.3 啥时候用

1.3.4 实例

1.4 代理模式

1.4 .1 使用场景

1.4.2 静态代理

1.4.3 jdk动态代理

1.4.4 CGLIB动态代理

1.4.5 jdk与cglib区别


1 设计模式概述

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、减少代码。

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:

http://dl.iteye.com/upload/attachment/0083/1179/57a92d42-4d84-3aa9-a8b9-63a0b02c2c36.jpg?_=3023236

1.1 设计模式的六大原则

1.1.1开闭原则(Open Close Principle)——扩展性、易于修改

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

1.1.2、里氏代换原则(Liskov Substitution Principle)——面向对象

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

1.1.3、依赖倒转原则(Dependence Inversion Principle)——面向接口编程

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

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

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

1.1.5、迪米特法则(最少知道原则)(Demeter Principle)

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

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

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

1.2 单例模式

保证jvm中只能有一个实例。本质是控制实例数目

懒汉式:线程不安全,需要时才会创建,双重校验锁可实现线程安全

饿汉式:线程安全,class加载时候就初始化

1.2.1  为什么使用单例

1)如果某个实体类(需要加载配置文件)被创建多次的话,那么就会重复获取配置数据,浪费程序运行时间

2)并且每个创建的对象实例都会缓存这些配置数据,浪费内存空间

3)同一个类中,既有实现接口(抽象类)要求的对外功能,又有内部实现获取配置数据的和缓存数据的功能,职责不够单一

1.2.2 知识要点

1)运行期间只会被创建一个类实例

2)提供一个全局唯一访问这个类实例的全局访问点

3)关心创建,不关心业务

4)虚拟机范围类单例,不适用于集群

5)注意线程安全性

6)懒汉式、饿汉式、缓存、双重检查枷锁、lazy initialization holder class、枚举

7)体检延迟加载、缓存设计思想

8)容易扩展到多实例控制

懒汉式代码:

class Singleton{
    static private Singleton singleton;
    //懒汉式
    private Singleton(){
    }
    //返回一个实例
    static public Singleton getInstance(){
        if(singleton==null)
            singleton=new Singleton();
        return singleton;
    }
}
public class Test001 {
    public static void main(String[] args) {
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        System.out.println(s1==s2);
    }
}
输出:
true

直接通过在getInstance()方法加synchronized也可以解决线程安全,不过效率很低,因为他保证只有一个线程可以进入该方法,但是如果singleton已经不为Null了,他还是只允许一个线程进入该方法然后再返回该类的实例,这是不是就有点蛋疼了?

双重检验锁:

class Singleton{
    static private Singleton singleton;
    //懒汉式 双重检验锁
    private Singleton(){
    }
    //返回一个实例
    static public Singleton getInstance(){
        if(singleton==null){//第一层上锁
            synchronized (Singleton.class){
                if(singleton==null)//第二层上锁
                    singleton=new Singleton();
            }
        }
        return singleton;
    }
}
public class Test001 {
    public static void main(String[] args) {
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        System.out.println(s1==s2);
    }
}

饿汉式:

class Singleton1{
    static private Singleton1 singleton=new Singleton1();
    //饿汉式 当class文件被加载的时候,就已经初始化了对象
    private Singleton1(){
    }
    //返回一个实例
    static public Singleton1 getInstance(){
        return singleton;
    }
}
public class Test002 {
    public static void main(String[] args) {
        Singleton1 s1=Singleton1.getInstance();
        Singleton1 s2=Singleton1.getInstance();
        System.out.println(s1==s2);
    }
}

1.3 工厂模式

实现创建者和调用者分离。提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例类型可以是接口、抽象类以及具体的类,本质是选择实现

1.3.1 为什么

向模块外部提供了接口,可是外部根本不知道模块内部的具体实现,那么外部模块如何来获取一个实现接口的实现对象呢?

1.3.2 知识要点

1)位于对外提供接口的模块内

2)目的用来创建对象实例,被创建对象可以是接口、抽象类以及普通的类

3)可以实现为单例也可以是静态工厂

4)“选择合适的实现”

5)选择时,需要的参数可以从客户端传入、配置文件或者程序运行期的某个运行结果等

6)如果使用反射+配置文件的方式,可以写出通用的简单工厂

1.3.3 啥时候用

1)完全封装隔离具体实现,外部仅能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取相应的接口,无须关心具体实现

2)集中管理和控制对外创建对象

1.3.4 实例

可以看到main里面对AodiCar的使用是通过Factory类获取的,所以实现了分离

interface Car{
    void run();
}

class AodiCar implements Car{

    @Override
    public void run() {
        System.out.println("创建奥迪");
    }
}
class BenChiCar implements Car{

    @Override
    public void run() {
        System.out.println("创建奔驰");
    }
}

class Factory {
   static public Car createCar(String name){
        Car car=null;
        switch (name){
            case "奥迪":
                car=new AodiCar();
                break;
            case "奔驰":
                car=new BenChiCar();
                break;
                default:
                    break;
        }
        return car;
    }
}
public class Test003 {
    public static void main(String[] args) {
        Car car1=Factory.createCar("奥迪");
        Car car2=Factory.createCar("奔驰");
        car1.run();
        car2.run();
    }
}

1.4 代理模式

类似于买方卖方中的中介(买卖双方仅和中介交流即可),通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用前处理或者调用后处理,保证了真是角色的安全性。既(AOP微实现)  ,AOP核心技术面向切面编程。常用于AOP、日志打印、事物、权限控制等场景。

分为两种类型,其中静态代理需要创建代理类,动态代理不需要创建代理类,而动态代理又有jdk和Cglib 、javaassist(字节码操作库)动态代理

1.4 .1 使用场景

1)安全代理,可以屏蔽真是角色

2)远程代理,远程调用代理类RMI

3)延迟加载,先加载轻量级代理类,真正需要再加载真实类

1.4.2 静态代理

小明买房(中介Proxy代替小明买房)

interface House{
    void maifang();
}

class XiaoMing implements House{
    @Override
    public void maifang() {
        System.out.println("小明,买房咯!!!!!");
    }
}

class Proxy implements House{
    //代理对象
    private XiaoMing xiaoMing;
    public Proxy(XiaoMing xiaoMing){
        this.xiaoMing=xiaoMing;
    }
    @Override
    public void maifang() {
        System.out.println("中介,开始替你买房");
        xiaoMing.maifang();
        System.out.println("中介,替你买房结束");
    }
}
public class StaticHouse {
    public static void main(String[] args) {
        House house = new Proxy(new XiaoMing());
        house.maifang();
    }
}

缺点:新增一个小狗,岂不是又要添加一个代理类?????

1.4.3 jdk动态代理

interface House{
    void maifang();
}

class XiaoMing implements House{
    @Override
    public void maifang() {
        System.out.println("小明,买房咯!!!!!");
    }
}

//jdk动态代理
class JdkProxy implements InvocationHandler{
    private Object object;

    public JdkProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介,开始替你买房");
        Object invoke = method.invoke(object, args);
        System.out.println("中介,替你买房结束");
        return invoke;
    }
}
public class StaticHouse {
    public static void main(String[] args) {
        XiaoMing xiaoMing = new XiaoMing();
        JdkProxy jdkProxy = new JdkProxy(xiaoMing);
        House house = (House) Proxy.newProxyInstance(xiaoMing.getClass().getClassLoader(), xiaoMing.getClass().getInterfaces(), jdkProxy);
        house.maifang();
    }
}

1.4.4 CGLIB动态代理

public class Cglib implements MethodInterceptor {

	@Override
	public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("我是买房中介 , 开始监听你买房了....");
		Object invokeSuper = methodProxy.invokeSuper(o, args);
		System.out.println("我是买房中介 , 开结束你买房了....");
		return invokeSuper;

	}

}

class Test22222 {
	public static void main(String[] args) {
		Cglib cglib = new Cglib();
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(XiaoMing.class);
		enhancer.setCallback(cglib);
		Hose hose = (Hose) enhancer.create();
		hose.mai();
	}
}

1.4.5 jdk与cglib区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP 

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值