目录
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 设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、减少代码。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:
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之间转换