-
设计模式的七大原则这种方式,Person的receive方法依赖了具体的类Email,导致receive函数只能接受Email信息,无法扩展这种Person类依赖接口Message,就可以带入不同的实现类
-
单一职责原则
-
对于类来说,一个类应该只负责一项职责,如类A负责职责1和职责2,在对职责1进行需求变更时,可能影响职责2
-
如果类的方法比较少,可以在类方法上维持单一职责原则
-
-
接口隔离原则(Interface Segregation Principle)
-
-
B,D是接口的实现类,A通过接口依赖B的实现方法oprtation1,oprtation2,oprtation3.C通过接口依赖D的实现方法oprtation1,oprtation4,oprtation5. 由于B中实现的oprtation4,oprtation5没有用,D中实现的oprtation2, oprtation3 没有用。造成浪费,这就违反了接口隔离原则,需要将inteface1分离出多个接口
-
-
-
依赖倒转原则(Dependency Inversion Principle)
-
实现类不要依赖实现类(不继承实现类,函数参数、成员变量类型不能为实现类等),尽量依赖接口或者抽象类
-
-
-
里氏替换原则
-
-
假设父类为A,A有三个子类B,C,D。如果修改A类的方法,可能会导致B,C,D中的方法异常。
-
里氏替换原则的核心就是不重写父类已经实现好的方法
-
如果必须重写方法,那就通过依赖,聚合,组合等方法来解决
-
-
开闭原则(open closed principle)ocp原则
-
对提供方扩展开放,对使用方修改关闭
-
-
迪米特法则(Demeter Principle)
-
只与直接朋友通信
-
直接朋友:每个对象都会与其他对象有耦合关系,只要对象间有耦合关系,我们认为这两个对象是朋友关系。我们称出现在成员变量,方法返回值,方法参数中的类为直接朋友。陌生的类最好不要出现在类内部
-
-
合成复用原则(Composite Reuse Principle)
-
尽量使用组合/聚合的方式,而不是继承
-
例如A类要使用B类的方法,但是A与B之间没有很强的继承关系
-
方法一:继承(避免这种方式)
-
方式二:依赖
-
方式三:聚合 (把A聚合到B中去)
-
方式四:组合(把A组合到B中去)
-
-
-
设计模式
-
设计模式的分类
-
创建型模式: 单例模式,抽象工厂模式,原型模式,建造者模式, 工厂模式
-
结构型模式:适配器模式,桥接模式, 装饰模式,组合模式,外观模式,享元模式, 代理模式
-
行为型模式: 模板方法模式,命令模式,访问者模式,迭代器模式, 观察者模式,中介者模式,备忘录模式,解释器模式, 状态模式,策略模式,职责链模式
-
-
-
单例模式(强烈推荐volatile双重检查和 静态内部类 )
-
单例模式的使用场景
-
需要频繁创建和销毁的对象,创建对象耗时过多或者消耗资源过多(重量级对象),但又经常用到的对象,工具类对象,频繁访问数据库或文件的对象。
-
-
饿汉式(静态常量,线程安全)
-
代码
-
public class Singleton {
-
private final static Singleton instance = new Singleton();
-
private Singleton() {}
-
public static Singleton getInstance() {
-
return instance;
-
}
-
}
-
-
由于在类初始化时new对象,所以线程安全
-
但是没有做到lazy loading,即可能因为其他原因(非调用getInstance方法)导致类加载而new单例,可能导致new出的单例从来没有使用过。导致内存浪费
-
-
饿汉式(静态代码块,线程安全)
-
代码
-
public class Singleton {
-
private final static Singleton instance;
-
static {
-
instance= new Singleton();
-
}
-
private Singleton() {}
-
public static Singleton getInstance() {
-
return instance;
-
}
-
}
-
-
线程安全,但是浪费内存
-
-
枚举(饿汉式)
-
枚举知识点
-
枚举是一个类,可以包含类的所有形式,例如构造方法,成员方法。但是枚举的成员变量是该枚举类的实例
-
下面两种方式是等价的
-
-
单例代码
-
public enum Singleton{
-
INSTANCE;
-
}
-
-
属于饿汉式的一种,类加载就会初始化单例对象,线程安全,消耗内存
-
-
懒汉式(线程不安全)
-
代码
-
public class Singleton {
-
private static Singleton instance;
-
private Singleton() {}
-
public static Singleton getInstance() {
-
if( instance== null)
-
instance= new Singleton();
-
return instance;
-
}
-
}
-
-
省内存,线程不安全
-
-
懒汉式(线程安全,同步方法)
-
代码
-
public class Singleton {
-
private static Singleton instance;
-
private Singleton() {}
-
public static synchronized Singleton getInstance() {
-
if( instance== null)
-
instance= new Singleton();
-
return instance;
-
}
-
}
-
-
省内存,线程安全,但是synchronized开销过大
-
-
懒汉式(同步代码块)
-
代码
-
public class Singleton {
-
private static Singleton instance;
-
private Singleton() {}
-
public static Singleton getInstance() {
-
if( instance== null) {
-
synchronized(Singleton. class) {
-
instance= new Singleton();
-
}
-
}
-
return instance;
-
}
-
}
-
-
线程不安全,thread2,thread3都在 synchronized阻塞,thread1 new完实例,释放锁,thread1,thread2仍可以进去new实例
-
-
双重检查(懒汉式)
-
代码
-
public class Singleton {
-
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;
-
}
-
}
-
-
注意一定要用 volatile 修饰,不然当一个线程new出对象,其他线程无法感知
-
线程安全,不浪费内存,开销小
-
-
静态内部类(懒汉式)
-
代码
-
public class Singleton {
-
private Singleton() {}
-
private static class SingletonInstance{
-
private static final Singleton singleton = new Singleton();
-
}
-
public static Singleton getInstance(){
-
return SingletonInstance. singleton;
-
}
-
}
-
-
线程安全,不浪费内存,开销小
-
-
-
简单工厂模式(静态工厂模式)
-
使用场景
-
当一个产品有多个类别,我们使用简单工厂模式来创建某一类别的实例对象
-
-
优点
-
------>
-
如左图不使用简单工厂模式,如果新增一个披萨种类,则要修改PizzaStore1,PizzaStore2,PizzaStore3,这三个类;如果使用右侧方式,则只需要修改SimapleFactory类,如果SimpleFactory是利用反射创建Pizza实例,则SimpleFactory都不需要修改
-
-
-
工厂方法模式
-
抽象工厂模式
-
现在要开加盟店,有北京口味的Pizza,伦敦口味的Pizza,为了统一管理工厂类(减少if--else等),抽出抽象工厂
-
-
-
原型模式
-
动态的获取对象的运行时状态并复制一份
-
继承Clonable,实现clone方法
-
对象复制,person.clone会复制一个与person属性一样的实例对象
-
代码
-
class Person implements Cloneable{
-
String name;
-
int age;
-
public Object clone() throws CloneNotSupportedException {
-
Object obj= super.clone();
-
return obj;
-
}
-
}
-
-
-
浅拷贝
-
super.clone()属于浅拷贝
-
-
深拷贝
-
使用序列化和反序列化实现深拷贝(也可破解单例对象)
-
代码
-
class Person implements Serializable{String name;int age;Exam exam;public Person deepCopy() {ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;Person p= null;try {bos= new ByteArrayOutputStream();oos= new ObjectOutputStream( bos);oos.writeObject( this);bis = new ByteArrayInputStream( bos.toByteArray());ois = new ObjectInputStream( bis);p = (Person) ois.readObject();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {ois.close();bis.close();oos.close();bos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return p;}}
-
-
-
建造者模式
-
建造者模式的四个主要角色
-
Product(产品角色):一个具体的产品对象
-
Builder(抽象建造者):接口或者抽象类,定义了生产产品的流程
-
ConcreteBuilder(具体建造者):实现了Builder,重写了生产流程,不同的产品流程产生不同的Product
-
Director(指挥者),该指挥者像工厂一样,传入的参数不同,生产的货物不同
-
-
UML类图表示
-
-
建造者模式与工厂模式的区别
-
Director类相当于工厂模式
-
建造者模式是对产品创建方式的一种提取,在工厂模式产品的生产流程保留在产品内部,而建造者模式则把生产流程剥离
-
-
-
适配器模式
-
将一个类的接口转成另一种接口,让原本不兼容的类可以兼容,主要用于兼容性调整;Adapter类通过继承src类,实现dst类接口,完成src-->dst的转换
-
分为类适配器模式,对象适配器模式,接口适配器模式
-
类适配器模式
-
-
对象适配模式:根基合成复用原则,少用继承
-
-
接口适配模式
-
当我们实现接口时,必须实现接口的所有方法,但是我们不需要实现那么多方法时,可以先用一个抽象类实现接口的所有方法,这些方法可以是空方法。然后子类再有选择的实现部分方法
-
适用于一个接口不想使用其所有方法的情况
-
-
适配器模式使用举例(SpringMVC,减少if-else判断,支持ocp原则)
-
-
如果新增一个Controller的实现类,只需要新增一个Adapter,支持了ocp原则
-
-
-
-
桥接模式
-
解决类爆炸问题
-
-
可以把每次都需要复制的类抽出一个接口,聚合到另一个类中,这样新增一个手机款式,就不需要新增品牌
-
-
-
装饰着模式
-
动态的将新功能附加到对象上(增量更新新功能),可以解决类爆炸问题
-
-
-
如上图有不同种类的Coffee,Coffee可以加调料糖和巧克力;如果新增一个Coffee,代码不用改动
-
IO流就采用了这种方式,例如inputstream---ObjectOutputStream
-
-
组合模式
-
当我们要处理的对象是一颗树形的结构而我们要对树的叶子节点和非叶子节点进行操作时,它能提供一致的方式,而不用考虑该节点是不是叶子节点
-
-
-
HashMap采用了组合模式
-
-
外观模式
-
外观模式和工厂模式相类似。工厂模式旨在输出不同的对象实例,而外观模式旨在根据不同的条件,调用不同的方法。 符合ocp原则
-
-
-
享元模式(蝇量模式)
-
享元模式可以解决重复对象的内存浪费问题。
-
池技术是享元模式的经典利用场景。如String常量池,数据库连接池,线程池,缓冲池等
-
-
-
代理模式
-
被代理的对象可以是,远程对象,创建开销大的对象,需要安全控制的对象
-
代理有三种形式:静态代理,动态代理(JDK代理),Cglib(可以在内存中动态的创建对象,而不需要实现接口)
-
静态代理(和装饰器类似。但是代理类没有子类,装饰器还有具体的实现类)
-
-
动态代理
-
基本介绍
-
代理对象(proxy)不需要实现接口,被代理对象(target)需要实现接口
-
代理对象的生成是通过jdkAPI动态创建
-
-
JDK生成代理对象的API
-
Static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
-
loader:被代理类的类加载器
-
interfaces:被代理类实现的接口列表
-
handler = new InvocationHandler()
-
-
-
代码(ProxyFactory代码是固定的
-
public class Main {
-
public static void main(String[] args) {
-
ITeacherDAO target = new TeacherDAO();
-
ProxyFactory proxyFactory = new ProxyFactory( target);
-
ITeacherDAO proxy = (ITeacherDAO) proxyFactory.getProxyInstance();
-
proxy.teach();
-
}
-
}
-
interface ITeacherDAO{
-
void teach();
-
}
-
class TeacherDAO implements ITeacherDAO{
-
@Override
-
public void teach() {
-
// TODO Auto-generated method stub
-
System. out.println( "老师在上课");
-
}
-
-
}
-
class ProxyFactory{
-
private Object target;
-
public ProxyFactory( Object target) {
-
this. target= target;
-
}
-
public Object getProxyInstance() {
-
return Proxy. newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
-
-
@Override
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
// TODO Auto-generated method stub
-
System. out.println( "代理开始");
-
Object returnVal = method.invoke( target, args);//调用目标对象的方法,参数为args,参数proxy的作用
-
System. out.println( "代理结束");
-
return returnVal;
-
}
-
});
-
}
-
}
-
-
考虑以下问题,在Mybatis,Mapper接口是没有实现类的,为啥也可以实现动态代理
-
接口
-
public interface Inter {
-
public void fun();
-
}
-
-
Main函数
-
public static void main(String[] args) {
-
Inter inter = (Inter)Proxy. newProxyInstance(Inter. class.getClassLoader(), new Class[] {Inter. class}, new InvocationHandler() {
-
-
@ Override
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
// TODO Auto-generated method stub
-
System. out.println( "不需要实现类,我也进入了该函数");
-
return null;
-
}
-
});
-
inter.fun();
-
}
-
-
所以说动态代理不一定需要实现类,没有实现类也可以获得代理对象
-
-
-
Cglib代理
-
被代理对象实现接口,则用JDK代理;被代理对象未实现任何接口使用Cglib代理
-
Cglib动态的生成被代理类的子类,因此被代理类不能被final修饰
-
使用方法:
-
引入Cglib的jar包
-
asm.jar
-
asm-commons.jar
-
asm-tree.jar
-
cglib-2.2.jar
-
-
目标对象不能为final修饰。
-
如果被代理方法使用final/static修饰,则无法被拦截
-
-
-
代码
-
public class Main {public static void main(String[] args) {// TODO Auto-generated method stubTeacherDAO target = new TeacherDAO();ProxyFactory proxyFactory = new ProxyFactory( target);TeacherDAO proxy = (TeacherDAO) proxyFactory.getProxyInstance();proxy.teach();}}class TeacherDAO {public void teach() {System. out.println( "老师在教书");}}class ProxyFactory implements MethodInterceptor{Object target;public ProxyFactory(Object target) {// TODO Auto-generated constructor stubthis. target= target;}public Object getProxyInstance() {//1.创建工具类Enhancer enhancer = new Enhancer();//2.设置父类enhancer.setSuperclass( target.getClass());//3.设置回调函数enhancer.setCallback( this);//创建子类对象return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// TODO Auto-generated method stubSystem. out.println( "代理开始");Object returnVal = method.invoke( target, args);System. out.println( "代理结束");return returnVal;}}
-
代理模式的几种变体
-
防火墙代理:内网通过代理穿透防火墙,实现对公网的访问
-
缓存代理:当请求图片文件等资源时,先到缓存去取,如果缓存没有再到服务器去取
-
远程代理: 远程对象的本地代表,通过它可以把远程对象当作本地对象来调用,远程代理通过网络来和远程对象沟通
-
同步代理,主要在多线程中,完成同步工作
-
-
-
-
模板方法模式
-
在一个抽象类中公开定义了执行它的方法的模板,他的子类可以按照需要重写方法实现,但调用将以抽象类中定义的方式进行
-
-
钩子函数(如各种框架的生命周期函数)
-
在模板方法的父类中定义一个函数,该函数默认不做任何事情,子类可以视情况去重写他。
-
-
-
命令模式
-
将军发布命令,士兵去执行;将军是命令发布者,士兵是命令接收者,命令连接了将军和士兵。
-
Invoker是调用者(将军),Receiver是接收者(士兵),MyCommand是命令,实现了Command接口,持有接收对象
-
实现了调用者与被调用者之间的解耦
-
-
-
访问者模式
-
-
举个例子,假设有歌手唱歌,观众对他们投票。可以投success和fail。如果突然新增了一种投票方式弃权giveup,尽量不改代码,如何设计呢
-
-
只需要新增一个GiveUp类实现Vote接口即可。访问者模式将行为从主体中剥离
-
-
迭代器模式
-
提供一种遍历集合(List,Array,Set)元素的统一接口,用一致的方式遍历集合元素,而不需要知道集合是list还是set或者数组。
-
-
这里的ArrayIterator一般以内部类(内部类可以访问外部类的属性)的形式存在于ArrayTarget内。
-
-
观察者模式
-
-
代码
-
public class Observera {public static void main(String[] args) {// TODO Auto-generated method stubSubject sub = new WeatherData();Baidu baidu = new Baidu();baidu. name= "Baidu";Baidu sina = new Baidu();sina. name= "Sina";sub.regester( baidu);sub.regester( sina);sub.notifyAllObservers();}}//消息分发规则interface Subject{void regester(Observer obj);void remove(Observer obj);void notifyAllObservers();}//消息分发者一般为单例或者静态工具类class WeatherData implements Subject{private List<Observer> observers = new ArrayList<>();@Overridepublic void regester(Observer obj) {// TODO Auto-generated method stubthis. observers.add( obj);}@Overridepublic void remove(Observer obj) {// TODO Auto-generated method stubthis. observers.remove( obj);}@Overridepublic void notifyAllObservers() {// TODO Auto-generated method stubfor(Observer obs: observers)obs.update();}}//接受规则interface Observer{void update();}class Baidu implements Observer{String name;@Overridepublic void update() {// TODO Auto-generated method stubSystem. out.println( name+ "数据更新啦");}}
-
-
中介者模式
-
备忘录模式
-
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象外保存这个状态,这样就可以将该对象恢复到原来的状态
-
-
List<SnapShot>可以改为HashMap
-
-
解释器模式
-
给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言(表达式)
-
编译器,正则表达式,XML解释器等都是该原理
-
-
状态模式
-
主要用来解决对象在多种状态转换时,需要对外输出不同行为的问题,状态和行为是一一对应的,状态之间可以相互转换
-
-
-
还可应用于购物状态的切换
-
-
-
策略模式
-
定义算法族分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端。(把变化的行为从主体中剥离出来)
-
把变化的代码从不变的代码中剥离出来
-
定义策略接口,面向接口编程
-
多用组合/聚合,少用继承
-
-
-
JDK源码中Comparator接口就是策略接口
-
-
职责链模式
-
-
如一个请假流程,小于2天找项目经理审批,少于7天找项目主管审批。。。。
-
-
如果经理无法处理request,就调用Manager的handler对象来处理。
-
JAVA设计模式
最新推荐文章于 2023-06-10 18:46:24 发布