sdk对外暴露的接口不合理
我们经常使用一些sdk来完成我们的需求,但往往有些sdk对外暴露的接口并不合理,再加上如果这个sdk本身处于快速迭代期,每次变更某些api的话,业务方如果使用的地方较多,那么批量修改 其实也是比较麻烦的。
看看如何针对上述情况做一些改进
看一段示例代码:
public class SchoolSystemSdk {
/** * 存储学生 * * @param name 姓名 * @param age 年纪 * @param No 学号 */
public void saveStudent(String name, int age, int No) {
}
/** * 存储教师 * * @param name 教师姓名 * @param studentNumber 学生数量 * @param schoolName 学校名称 */
public void saveTeacher(String name, int studentNumber, String schoolName) {
}
}
public class TestMain {
public static void main(String[] args) {
SchoolSystemSdk schoolSystemSdk=new SchoolSystemSdk();
schoolSystemSdk.saveStudent("wuyue",18,12345);
schoolSystemSdk.saveTeacher("wuyanzu",18,"南京大学");
}
}
问题就是这个sdk对外提供的函数参数过多,没有封装性,且可以很容易想到日后这个sdk如果新增参数的话,我们的业务代码肯定要批量修改调用的地方,很是蛋疼。但是我们又没有权限修改sdk的源码(暂不考虑asm等字节码技术)
下面进行修改: 首先定义一下学生和老师
class Student {
String name;
int age;
int No;
}
class Teacher {
String name;
int studentNumber;
String schoolName;
}
再定义一个接口 ,注意接口的参数
public interface ISchoolSystem {
void saveStudent(Student student);
void saveTeacher(Teacher teacher);
}
然后定义我们的主类 注意类的声明
//extends了sdk的类 且实现了接口
public class SchoolSystemWrapper extends SchoolSystemSdk implements ISchoolSystem {
@Override
public void saveStudent(Student student) {
saveStudent(student.name, student.age, student.No);
}
@Override
public void saveTeacher(Teacher teacher) {
//懒癌发作 就此省略
}
}
最后调用
public class TestMain {
public static void main(String[] args) {
ISchoolSystem iSchoolSystem = new SchoolSystemWrapper();
//懒癌发作 这边构造函数 就没有传参了,大家自行领会意图
iSchoolSystem.saveStudent(new Student());
iSchoolSystem.saveTeacher(new Teacher());
}
}
很简单的几步 就可以把我们的痛点全部解决了。 即解决了原生sdk对外暴露函数参数不好的问题,也可以解决日后sdk更新时 多处修改的问题,日后有修改也只要在wrapper那里 改一处即可
功能类似的第三方sdk 统一管理
这个场景在业务中也不少见,比如某些需求 我们需要使用不同的第三方sdk 做处理,我们希望收拢这些不同第三方sdk的接口,然后对外暴露出一个简单的接口。
举例说明: 一段文本,我们需要过滤掉黄色信息,少儿不宜的信息,以及不符合政策法规的信息。 让我们看看这段代码应该如何写:
//过滤掉不良的黄色信息
public class AWordsFilter {
public String filterSexyWords(String text) {
return "";
}
}
//过滤掉政治信息
class BWordsFilter {
public String filterPoliticalWords(String text) {
return "";
}
}
//过滤掉工信部给的敏感信息 senseWords 为敏感词
class CWordsFilter {
public String filterSenselWords(String text, String senseWords) {
return "";
}
}
使用他
public class TestMain {
public static void main(String[] args) {
//原始文本
String text = "1231231asdasdasdasda";
//三种过滤规则
AWordsFilter aWordsFilter = new AWordsFilter();
BWordsFilter bWordsFilter = new BWordsFilter();
CWordsFilter cWordsFilter = new CWordsFilter();
//过滤成最终想要的信息
String t1 = aWordsFilter.filterSexyWords(text);
String t2 = bWordsFilter.filterPoliticalWords(t1);
String t3 = cWordsFilter.filterSenselWords(t2, "蛤蛤");
}
}
开始优化代码 首先定义2个接口:
public interface IWordsFilter {
String filter(String text);
}
public interface IWordsSenseFilter extends IWordsFilter {
String filter(String text, String sense);
}
然后来实现他们:
public class AWordsFilterAdaptor implements IWordsFilter {
AWordsFilter aWordsFilter;
@Override
public String filter(String text) {
return aWordsFilter.filterSexyWords(text);
}
}
class BWordsFilterAdaptor implements IWordsFilter {
BWordsFilter bWordsFilter;
@Override
public String filter(String text) {
return bWordsFilter.filterPoliticalWords(text);
}
}
class CWordsFilterAdaptor implements IWordsSenseFilter {
CWordsFilter cWordsFilter;
@Override
public String filter(String text, String sense) {
return cWordsFilter.filterSenselWords(text, sense);
}
@Override
public String filter(String text) {
return null;
}
}
最后统一管理
class FilterManager {
List<IWordsFilter> filters = new ArrayList<>();
public void addFilterType(IWordsFilter iWordsFilter) {
filters.add(iWordsFilter);
}
public String filterAll(String text, String sense) {
String maskText = text;
for (IWordsFilter iWordsFilter : filters) {
if (iWordsFilter instanceof IWordsSenseFilter) {
maskText = ((IWordsSenseFilter) iWordsFilter).filter(maskText, sense);
} else {
maskText = iWordsFilter.filter(text);
}
}
return maskText;
}
}
看下使用效果,是不是可以达到收拢的效果
String text = "1231231asdasdasdasda";
FilterManager filterManager=new FilterManager();
filterManager.addFilterType(new AWordsFilterAdaptor());
filterManager.addFilterType(new BWordsFilterAdaptor());
filterManager.addFilterType(new CWordsFilterAdaptor());
filterManager.filterAll(text,"蛤蛤");
到这里应该可以理解,这种adapter模式的写法,就是特别适合如下场景: 一种事后的补救策略,你无法控制sdk的代码质量,或者说虽然不是sdk代码,但是老代码坑比较多,一时半会不好大改,但是可以通过这种adapter的方式来达到修正你业务代码质量的目标
非功能性需求如何与业务代码做分离
平时开发中我们应该经常碰到类似的需求,比如说做了一些需求,但是要针对这些需求做监控,日志,埋点,统计,鉴权等等,这些东西的开发本质上和我们的业务开发 是分离的,不应该将他们的代码写在一起。那么如何做分离呢? 这里举一个例子
假设我们这里有一个用户的登录注册功能
public class UserController {
public void login() {
}
public void register() {
}
}
现在要求对这个登录注册做一些监控,比如记录一下登录和注册行为发起的时间,记录下用户的id等等。 直接将这些代码写到我们本身的登录注册业务中 是极其不优雅 也是不好维护的。
如果我们懒一点,不想修改我们的UserController源码 那就用extends的方式来做
class UserControllerProxy extends UserController
{
@Override
public void login() {
//在这里做一些监控的操作 代码省略
super.login();
}
@Override
public void register() {
//在这里做一些监控的操作 代码省略
super.register();
}
}
勤快一点 ,可以定义接口来做
public class UserController implements IUserCon {
public void login() {
}
public void register() {
}
}
interface IUserCon {
void login();
void register();
}
//数据统计
class DataStatic {
public void doSome() {
}
}
class UserControllerProxy implements IUserCon {
public UserControllerProxy(IUserCon iUserCon) {
this.iUserCon = iUserCon;
}
private IUserCon iUserCon;
private DataStatic dataStatic = new DataStatic();
@Override
public void login() {
//在这里做一些监控的操作 代码省略
dataStatic.doSome();
iUserCon.login();
}
@Override
public void register() {
//在这里做一些监控的操作 代码省略
dataStatic.doSome();
iUserCon.register();
}
}
到这里还没完噢,还有一种情况,就是如果说被我们代理的类页面方法很多,我们如果用上述的方法 来做proxy就真的很愚蠢了,因为你有大量的重复代码 要写在不同的函数中。 又是复制粘贴的做法了。
另外你想,如果说你这种埋点或者是监控类的需求比较多,那被代理的类 就很多了,不止登录注册啊,还有其他模块都要使用,你是不是要手动添加更多的Proxy类?万一哪天这些埋点的方法有变动,想想要改多少地方?
要解决这个问题 也很简单
动态代理 动态生成我们的类,动态的修改Proxy中的代码 即可
public class UserDynamicProxy implements InvocationHandler {
DataStatic dataStatic;
public UserDynamicProxy(Object target) {
this.target = target;
dataStatic = new DataStatic();
}
Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
dataStatic.doSome();
Object result = method.invoke(target, args);
return result;
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
使用起来也比较简单
UserDynamicProxy dynamicProxy = new UserDynamicProxy(new UserController());
IUserCon iUserCon = dynamicProxy.getProxy();
iUserCon.register();
如果你在项目中恰好遇到这种需求,可以多用用动态代理,很强的功能,极简的写法。原理在本篇就不做过多介绍了, 不是本文的重点。有兴趣的可以参见动态代理原理
最后强调一下: 代理模式常用在业务系统中开发一些非功能性需求,比如:监控、统计、鉴权、限流、埋点、日志。我们将这些附加功能与业务功能解耦,放到代理类统一处理,让业务开发的同学只关注业务,不需要关注基础功能。 在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问,而非加强功能
java IO 中的设计哲学
java io可能是java 体系里面最复杂的一个模块了,经常用,但是很少有人能讲清楚为啥这么设计,今天我们就扒了它的皮,看看能从中学习到什么
首先看一张老图, 应该很多人都很熟悉了
我们现在尝试读取一个文件,为了提高效率我们使用缓存buffer
InputStream in=new FileInputStream("F:\\Java huashan.pdf");
InputStream bin=new BufferedInputStream(in);
大家有没有发现 这样写有什么异样?为啥一定要用一个inputStream去初始化一个bufferins呢,为啥fileins可以直接传个path就创建,bufferins就不行?
bufferins的构造函数:
看看fileins的构造函数
所以这里问题就来了,为什么bufferins这个类的构造函数是ins作为参数,而不能直接设置成像fileins一样, 直接接受一个path作为参数?
我们直接这样写
InputStream in=new BufferedFileInputStream("F:\\Java huashan.pdf");
不好吗?
我们再回头看看之前的ins的子类 他是不止一个fileins子类的,他还有其他多个子类。如果我们单独在fileins后面派生一个bufferfins的子类,那么其他子类要使用这个功能 也得派生出对应的bufferins子类。这样就显的非常冗余了。最终的结果就是子类爆炸
所以在java的世界里面是有一个基本设计原则的: 组合大于集成
所以在java io世界里 很多类都不是直接派生自InputStrem 而是持有ins的一个实例
就像一个Wrapper 包裹住他们一样。
细心的人会发现 Bufferins与DataIns 都不是直接extends的ins啊 而是extends的
这又是什么设计?
众所周知的是我们的ins 是一个抽象类
public abstract class InputStream implements Closeable {
所以为了让我们增强功能的io类,比如bufferins datainputs 只实现一些他们增强功能的方法,哪些没有变动的方法自然就放到filterins里面默认实现他了。
这里还会有人问 既然inputstream是一个抽象类,那你说的默认实现为啥不在inputSteream里面写? 为啥要画蛇添足加一个filterins来写? 我们看个图
我们可以想一下,如果我们将默认的实现写在InputStream中,去掉这个filterIns层,
new Bufferins(new FileInput("path"))
这个时候我们Buffins 的那些ins默认的方法 我们必须要重写一遍 形如:
InputStrean ins;
public void f(){
ins.f()
}
因为如果我们不这么写,那我们bufferins的默认f方法走的就是ins中的默认实现 而不是我们传进来的FileInput实现了。
想清楚这点 我们就可以猜到FilterInput到底是什么作用了
所以你看
FilterInput这个类 帮我们解决的 不就是上述的痛点吗?可以避免我们再手动复制粘贴一次哪些默认的方法调用而已。
分析到这里我们就来总结一下java io世界中 所采用的装饰者wrapper写法 主要用来解决什么问题。
主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这是判断是否该用装饰器模式的一个重要的依据,他跟Proxy的写法是不同的**,Proxy主要是新增跟原来功能不相干的功能,而装饰者是增强原来的功能,或者说是增加类似的功能,但是带有优化的效果**。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。
今天的文章一文搞懂四种Wrapper方法快速重构你的代码分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22620.html