背景
项目中通过自定义异常类来对异常逻辑进行管理,在逻辑异常时每次如果都通过new的方式创建异常貌似不太优雅;
思路
我们可以考虑通过创建断言工具类来达到这一目标,枚举可以理解成在类里面预先会定义好若干个该类型的对象,这样就可以避免在代码中偏频繁的用new了;
把这个作为出发点,我们可以用枚举来管理异常类的对象, 其原理就是组合模式
自定义异常
这里我们定义两个主要的业务异常类,当然这个自定义异常类根据情况可以再设计
BusinessException
package com.xx.design.exception.base;
/**
* @author EDY
*/
public class BusinessException extends RuntimeException {
public BusinessException() {
super();
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
}
public BusinessException(Throwable cause) {
super(cause);
}
protected BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
AuthException
package com.xx.design.exception.base;
/**
* @author EDY
*/
public class AuthException extends RuntimeException {
public AuthException() {
super();
}
public AuthException(String message) {
super(message);
}
public AuthException(String message, Throwable cause) {
super(message, cause);
}
public AuthException(Throwable cause) {
super(cause);
}
protected AuthException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
设计断言接口Assert
以上为定义的两个基础异常类,下面我们就来编写我们的异常断言类,首先定义一个断言接口,主要抽象出需要提供的断言方法:
package com.xx.design.exception.ass;
public interface Assert {
/**
* 断言对象 obj 非空。如果对象 obj 为空,则抛出异常
* 异常信息 message 支持传递参数方式,避免在判断之前进行字符串拼接操作
*
* @param obj 待判断对象
* @param message 异常消息
*/
void assertNotNull(Object obj, String message);
/**
*
* @param obj
* @param message
*/
void assertNull(Object obj, String message);
/**
* 断言是否为真
*
* @param bool 条件
* @param message 异常消息
*/
void assertTrue(Boolean bool, String message);
}
设计异常生产者接口ExceptionProvider
然后定一个异常的生产者接口 ExceptionProvider,主要作用是创建异常对象,由于我们的业务异常设计成了RuntimeException的子类,那么我们定义一个生产RuntimeException 的接口
package com.xx.design.exception.ass;
/**
* @author EDY
*/
public interface ExceptionProvider {
/**
* 创建异常
*
* @param message 参数
* @return 异常
*/
RuntimeException newException(String message);
}
断言模板 AbstractAssert
接下来我们定义一个断言并抛异常的模板类 AbstractAssert,主要是把 Assert和ExceptionProvider组合起来,重写Assert里的方法(抽象出一套模板),然后视情况生产并抛出异常,而其中的生产异常就需要子类来实现了;
package com.xx.design.exception.ass;
import com.xx.design.exception.base.BusinessException;
/**
* @author EDY
*/
public abstract class AbstractAssert implements Assert, ExceptionProvider {
/**
* 断言对象 obj 非空。如果对象 obj 为空,则抛出异常
* 异常信息 message 支持传递参数方式,避免在判断之前进行字符串拼接操作
*
* @param obj 待判断对象
* @param message message占位符对应的参数列表
*/
@Override
public void assertNotNull(Object obj, String message) {
if (obj == null) {
throw newException(message);
}
}
/**
* 断言true
*
* @param bool 条件
* @param message 异常消息
*/
@Override
public void assertTrue(Boolean bool, String message) {
if (!(bool != null && bool)) {
throw new BusinessException(message);
}
}
/**
* 断言为null
*
* @param obj 待判断对象
* @param message 异常提示消息
*/
@Override
public void assertNull(Object obj, String message) {
if (obj != null) {
throw new BusinessException(message);
}
}
}
类图如下:
具体的Assert类
这类型的类主要职责是生产异常,不同的业务异常类可以对应一个Assert类,其继承AbstractAssert并实现 newException方法
BusinessExceptionAssert
package com.xx.design.exception.ass;
import com.xx.design.exception.base.BusinessException;
/**
* BusinessException 断言工具
*
* @author EDY
*/
public class BusinessExceptionAssert extends AbstractAssert {
/**
* 生成 BusinessException
*
* @param message 异常消息
* @return BusinessException
*/
@Override
public RuntimeException newException(String message) {
return new BusinessException(message);
}
}
类图如下
UnauthorizedExceptionAssert
package com.xx.design.exception.ass;
import com.xx.design.exception.base.AuthException;
/**
* 授权异常的断言类
*
* @author EDY
*/
public class UnauthorizedExceptionAssert extends AbstractAssert {
/**
* 生成 UnauthorizedException
*
* @param message 异常消息
* @return BusinessException
*/
@Override
public RuntimeException newException(String message) {
return new AuthException(message);
}
}
类图如下
至此,我们在项目中可以直接使用 BusinessExceptionAssert 和 UnauthorizedExceptionAssert 来进行断言了,结合开头说的使用枚举来管理断言类于是我们有了以下的设计:
Assert管理类
我们使用 Asserts(注意单词后面的s,不是Assert) 枚举类管理AbstractAssert 的子类,但Asserts 枚举类也要实现Assert 以便继承Assert 提供的接口;
Asserts的设计核心是内部一个 Assert ass; 属性,结合Asserts其构造方法将AbstractAssert 子类都纳入Asserts管理!
package com.xx.design.exception.ass;
/**
* @author EDY
* 所有的异常断言对象,后续可根据异常类中的增加而扩展
*/
public enum Asserts implements Assert {
/**
* 业务异常的断言对象
*/
BUSINESS_ASSERT(new BusinessExceptionAssert()),
/**
* 授权异常断言对象
*/
AUTH_ASSERT(new UnauthorizedExceptionAssert());
Assert ass;
Asserts(Assert azz) {
this.ass = azz;
}
@Override
public void assertNotNull(Object obj, String message) {
this.ass.assertNotNull(obj, message);
}
@Override
public void assertNull(Object obj, String message) {
this.ass.assertNull(obj, message);
}
@Override
public void assertTrue(Boolean bool, String message) {
this.ass.assertTrue(bool, message);
}
}
代码中的使用
package com.xx.design.exception;
import com.xx.design.exception.ass.Asserts;
/**
* @author EDY
*/
public class AssertsTest {
public static void main(String[] args) {
Object a = new Object();
Asserts.BUSINESS_ASSERT.assertNull(a, "a应该为null,实际上不为null,断言失败");
}
}
最终代码结构如下:
代码可以在: 【gitee】 https://gitee.com/aqu415/code-demo/tree/master/design/src/main/java/com/xx/design下载
over~~