开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
封装继承多态 重写(模版方法设计模式中)接口、抽象类
依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
设计模式的好处:使用设计模式可以重构架构代码、提高代码复用性、扩展性
1.单例模式
什么是单例模式?
单例模式确保一个类只有一个实例,并提供一个全局访问点,实现单例模式的方法是私有化构造函数,通过getInstance()方法实例化对象,并返回这个实例
保证在jvm中只有一个实例 幂等
JVM中如何保证实例的幂等问题 保证唯一性
单例模式的优缺点:
1.单例类只有一个实例
2.共享资源,全局使用
3.节省创建时间,提高性能
缺点:可能存在线程不安全的问题,解决线程安全问题
单例模式的七种写法:
1)饿汉模式
优点:先天性线程安全 为什么先天性 当类初始的时候,就会创建该对象
缺点:如果项目使用过多的饿汉式会发生问题,项目在启动的时候的时候会变的非常慢、存放在方法区占用内存比较大,如果用户不使用该对象的时候,也会被提前创建好
/**
* 单例模式:饿汉模式
*/
public class SingletonV1 {
private static SingletonV1 singletonV1 = new SingletonV1();
private SingletonV1(){}
public SingletonV1 getInstance() {
return singletonV1;
}
}
/**
* 懒汉式模式
*/
public class SingletonV2 {
//懒汉式 当真正需要使用该对象的时候才会被初始化
private static SingletonV2 singletonV2;
private SingletonV2(){}
/**
* 线程安全问题 在多线程情况下 可能会被初始化多次
*
* @return
*/
private static SingletonV2 getSingletonV2() {
//当第一次singLetonV2等于null情况才会被初始化
if (singletonV2 == null) {
singletonV2 = new SingletonV2();
}
return new SingletonV2();
}
}
/**
* 双重检验锁
*/
public class SingletonV2 {
//懒汉式 当真正需要使用该对象的时候才会被初始化
private static SingletonV2 singletonV2;
private SingletonV2(){}
/**
* 线程安全问题 在多线程情况下 可能会被初始化多次
*
* @return
*/
private static SingletonV2 getSingletonV2() {
//当第一次singLetonV2等于null情况才会被初始化
if (singletonV2 == null) {
synchronized(SingletonV2.class){
if (singletonV2 == null) {
singletonV2 = new SingletonV2();
}
}
}
return new SingletonV2();
}
}
2.策略模式
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。
1.环境(Context)角色:持有一个Strategy的引用。
2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或者抽象类。此角色给所有的具体策略类所需的接口。
3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
定义策略接口—->实现不同的策略类—->利用多态或其他方式调用策略
基于枚举+反射+工厂方式实现策略模式:
代码:
public enum PayEnumStrategy {
/**
* 支付宝支付
*/
ALI_PAY("com.mayikt.strategy.impl.AliPayStrategy"),
/**
* 银联支付
*/
UNION_PAY("com.mayikt.strategy.impl.UnionPayStrategy");
PayEnumStrategy(String className) {
this.setClassName(className);
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
/**
* class完整地址
*/
private String className;
}
public interface PayStrategy {
/**
* 定义策略模式共同行为算法骨架
* @return
*/
public String toPayHtml();
}
@Component
public class AliPayStrategy implements PayStrategy {
@Override
public String toPayHtml() {
return "调用支付宝接口....";
}
}
@Component
public class PayContextStrategy {
/**
* 使用枚举+工厂方式实现策略模式
*
* @param payCode
* @return
*/
public String toPayEnumHtml(String payCode) {
if (StringUtils.isEmpty(payCode)) {
return "payCode不能为空!";
}
PayStrategy payStrategy = StrategyFactory.getPayStrategy(payCode);
if (payStrategy == null) {
return "没有查询到该渠道信息";
}
// 子类重写父类..
return payStrategy.toPayHtml();
}
}
@RestController
public class StrategyController {
@Autowired
private PayContextStrategy payContextStrategy;
@RequestMapping("/toPayHtml")
public String toPayHtml(String payCode) {
return payContextStrategy.toPayDbHtml(payCode);
}
}
public class StrategyFactory {
public static PayStrategy getPayStrategy(String strategyType) {
try {
// 1.获取枚举中className
String className = PayEnumStrategy.valueOf(strategyType).getClassName();
// 2.使用java反射技术初始化类
return (PayStrategy) Class.forName(className).newInstance();
} catch (Exception e) {
return null;
}
}
}
基于反射+数据库实现策略模式:直接通过数据库存入beanId的方式
public interface PayStrategy {
/**
* 定义策略模式共同行为算法骨架
* @return
*/
public String toPayHtml();
}
@Component
public class AliPayStrategy implements PayStrategy {
@Override
public String toPayHtml() {
return "调用支付宝接口....";
}
}
@Component
public class PayContextStrategy {
/**
* 使用枚举+工厂方式实现策略模式
*
* @param payCode
* @return
*/
public String toPayEnumHtml(String payCode) {
if (StringUtils.isEmpty(payCode)) {
return "payCode不能为空!";
}
PaymentChannelEntity paymentChannel = paymentChannelMapper.getPaymentChannel(payCode);
if(paymentChannel==null){
return "没有查询到该渠道信息";
}
String strategyBeanId = paymentChannel.getStrategyBeanId();
if(strategyBeanId==null){
return "没有配置策略BeanId";
}
// 使用beanid从容器获取对象
PayStrategy payStrategy= SpringUtils.getBean(strategyBeanId,PayStrategy.class);
return payStrategy.toPayHtml();
}
}
@RestController
public class StrategyController {
@Autowired
private PayContextStrategy payContextStrategy;
@RequestMapping("/toPayHtml")
public String toPayHtml(String payCode) {
return payContextStrategy.toPayDbHtml(payCode);
}
}
public interface PaymentChannelMapper {
@Select("\n" +
"SELECT id as id ,CHANNEL_NAME as CHANNELNAME ,CHANNEL_ID as CHANNELID,strategy_bean_id AS strategybeanid\n" +
"FROM payment_channel where CHANNEL_ID=#{payCode}")
public PaymentChannelEntity getPaymentChannel(String payCode);
}
@Data
public class PaymentChannelEntity {
/** ID */
private Integer id;
/** 渠道名称 */
private String channelName;
/** 渠道ID */
private String channelId;
/**
* 策略执行beanId
*/
private String strategyBeanId;
}
3.工厂模式
什么是工厂模式
实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式
工厂模式好处
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
利用工厂模式可以降低程序的耦合性,为后期的维护修改提供了很大的便利。
将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
工厂模式分类
简单工厂模式:
简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
public interface Car {
public void run();
}
public class AoDi implements Car {
public void run() {
System.out.println("我是奥迪汽车..");
}
}
public class JiLi implements Car {
public void run() {
System.out.println("我是吉利汽车...");
}
}
public class CarFactory {
public static Car createCar(String name) {
if (StringUtils.isEmpty(name)) {
return null;
}
if(name.equals("奥迪")){
return new AoDi();
}
if(name.equals("吉利")){
return new JiLi();
}
return null;
}
}
public class Client01 {
public static void main(String[] args) {
Car aodi =CarFactory.createCar("奥迪");
Car jili =CarFactory.createCar("吉利");
aodi.run();
jili.run();
}
}
单工厂的优点/缺点
优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则
工厂方法模式
工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
public interface Car {
public void run();
}
public class AoDi implements Car {
public void run() {
System.out.println("我是奥迪汽车..");
}
}
public class JiLi implements Car {
public void run() {
System.out.println("我是吉利汽车...");
}
}
public class JiLiFactory implements CarFactory {
public Car createCar() {
return new JiLi();
}
}
public class AoDiFactory implements CarFactory {
public Car createCar() {
return new AoDi();
}
}
public class Client {
public static void main(String[] args) {
Car aodi = new AoDiFactory().createCar();
Car jili = new JiLiFactory().createCar();
aodi.run();
jili.run();
}
}
抽象工厂模式
抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。
//发动机
public interface Engine {
void run();
void start();
}
class EngineA implements Engine {
public void run() {
System.out.println("转的快!");
}
public void start() {
System.out.println("启动快,自动档");
}
}
class EngineB implements Engine {
public void run() {
System.out.println("转的慢!");
}
public void start() {
System.out.println("启动快,手动档");
}
}
//座椅
public interface Chair {
void run();
}
class ChairA implements Chair{
public void run() {
System.out.println("可以自动加热!");
}
}
class ChairB implements Chair{
public void run() {
System.out.println("不能加热!");
}
}
public interface CarFactory {
// 创建发动机
Engine createEngine();
// 创建座椅
Chair createChair();
}
public class JiLiFactory implements CarFactory {
public Engine createEngine() {
return new EngineA();
}
public Chair createChair() {
return new ChairA();
}
}
public class Client002 {
public static void main(String[] args) {
CarFactory carFactory=new JiLiFactory();
Engine engine=carFactory.createEngine();
engine.run();
engine.start();
}
}
简单工厂、工厂方法、抽象工厂之小结、区别
简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品) 抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)
4.代理模式
简介
通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。既(AOP微实现) ,AOP核心技术面向切面编程。
代理模式应用场景
SpringAOP、事物原理、日志打印、权限控制、远程调用、安全代理 可以隐蔽真实角色
代理的分类
静态代理(静态定义代理类)
动态代理(动态生成代理类)
Jdk自带动态代理
Cglib 、javaassist(字节码操作库)
静态代理
由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
静态代理代码:
public interface IUserDao {
void save();
}
public class UserDao implements IUserDao {
public void save() {
System.out.println("已经保存数据...");
}
}
代理类
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao iuserDao) {
this.target = iuserDao;
}
public void save() {
System.out.println("开启事物...");
target.save();
System.out.println("关闭事物...");
}
}
动态代理
简介:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK动态代理:
1)原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)
2)实现方式:
1. 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4. 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口
// 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
// 通过构造函数传入目标对象
public InvocationHandlerImpl(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("调用开始处理");
result = method.invoke(target, args);
System.out.println("调用结束处理");
return result;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 被代理对象
IUserDao userDao = new UserDao();
InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
ClassLoader loader = userDao.getClass().getClassLoader();
Class<?>[] interfaces = userDao.getClass().getInterfaces();
// 主要装载器、一组接口及调用处理动态代理实例
IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
newProxyInstance.save();
}
}
CGLIB动态代理:
原理:利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码
相关代码:
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
public Object getInstance(Object target) {
// 设置需要创建子类的类
this.targetObject = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事物");
Object result = proxy.invoke(targetObject, args);
System.out.println("关闭事物");
// 返回代理对象
return result;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
userDao.save();
}
}
CGLIB动态代理与JDK动态区别
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
Spring中。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。 CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
性能问题:由于Cglib代理是利用ASM字节码生成框架在内存中生成一个需要被代理类的子类完成代理,而JDK动态代理是利用反射原理完成动态代理,所以Cglib创建的动态代理对象性能比JDk动态代理动态创建出来的代理对象新能要好的多,但是对象创建的速度比JDk动态代理要慢,所以,当Spring使用的是单例情况下可以选用Cglib代理,反之使用JDK动态代理更加合适。同时还有一个问题,被final修饰的类只能使用JDK动态代理,因为被final修饰的类不能被继承,而Cglib则是利用的继承原理实现代理的。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。