结构型设计在工作中的一些经验总结

简介: 结构型设计在工作中的一些经验总结

关于设计模式的一些实战总结 – 常见的结构型设计模式


在设计模式里面,有一种叫做适配器的设计模式 Adapter Design Pattern ,这类适配器模式通常应用于做不同接口之间的适配和调整,常见的应用场景例如:


对一些不同实现的接口做统一整合,对一些接口的设计“缺陷”做一定的补救措施。


举个栗子来说,假设某个业务场景里面的遇到了一个人脸识别的功能:


公司内部接入了多个第三方的认证接口,具体的接口设计如下:


public interface IFaceRecognitionService {
    /**
     * 人脸识别
     *
     * @param userId
     * @return
     */
    Boolean recognition(Integer userId);
}
复制代码


对应的人脸认证接口实现如下:


public class AFaceRecognitionService implements IFaceRecognitionService {
    @Override
    public Boolean recognition(Integer userId) {
        System.out.println("this is AliFaceRecognitionService");
        return true;
    }
}
public class BFaceRecognitionService implements IFaceRecognitionService {
    @Override
    public Boolean recognition(Integer userId) {
        System.out.println("this is B FaceRecognitionService");
        return true;
    }
}
复制代码


然后此时我们就有两类的认证接口,假设后边的业务愈发扩展,接入的第三方接口越来越多,这时候可以设计出一个灵活的适配器来进行代码的兼容:


public class FaceRecognitionAdaptorService implements IFaceRecognitionService {
    private IFaceRecognitionService faceRecognitionService;
    public FaceRecognitionAdaptorService(IFaceRecognitionService faceRecognitionService){
        this.faceRecognitionService = faceRecognitionService;
    }
    public FaceRecognitionAdaptorService(){
    }
    public IFaceRecognitionService select(int type){
        if(type==1){
            this.faceRecognitionService = new AFaceRecognitionService();
        }else{
            this.faceRecognitionService = new BFaceRecognitionService();
        }
        return this.faceRecognitionService;
    }
    @Override
    public Boolean recognition(Integer userId) {
        return faceRecognitionService.recognition(userId);
    }
    public static void main(String[] args) {
        FaceRecognitionAdaptorService faceRecognitionAdaptorService = new FaceRecognitionAdaptorService();
        faceRecognitionAdaptorService.select(1).recognition(1001);
        faceRecognitionAdaptorService.select(2).recognition(1002);
    }
}
复制代码


虽然说demo很简单,但是从代码的后期维护角度来说,我们可以得出以下两点经验:


1.使用了适配器模式其实有时候可以让两个独立的类各自发展,隔离他们之间的依赖,每当有类发生变化的时候只会影响到对应的类和适配器内部的代码,耦合程度可以大大降低。


代理模式的应用


什么是代理模式?


简单来讲就是在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。我们通过一个简单的例子来学习一下代理模式:


一个用户登录的接口代码如下所示:


public class UserService implements IUserService{
    @Override
    public void login() {
        System.out.println("UserService login...");
    }
}
复制代码


可以结合代理模式,使用jdk代理的方式来进行接口的时长统计:


public class MetricsProxy {
    public Object createProxy(Object proxyObj){
        Class<?>[] interfaces = proxyObj.getClass().getInterfaces();
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(interfaces);
        return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(), interfaces, dynamicProxyHandler);
    }
    private class DynamicProxyHandler implements InvocationHandler {
        private Object proxiedObject;
        public DynamicProxyHandler(Object proxiedObject) {
            this.proxiedObject = proxiedObject;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long begin = System.currentTimeMillis();
            Object result = method.invoke(proxiedObject, args);
            String apiName = proxiedObject.getClass().getName() + ":" + method.getName();
            long end = System.currentTimeMillis();
            System.out.println("接口耗时:"+(end - begin));
            return result;
        }
    }
    public static void main(String[] args) {
        MetricsProxy metricsProxy = new MetricsProxy();
        IUserService userService = (IUserService) metricsProxy.createProxy(new UserService());
        userService.login();
    }
}
复制代码


除了上述的这种简单场景之外,实际上在我们工作中经常应用的rpc服务框架也有代理模式的影子。


例如说我们常用的dubbo框架,在进行远程化的服务调用过程中就是使用了代理模式的方式进行设计,使得用户端在调用接口的时候不需要去对底层的一些网络通信,数据编码做过多深入的了解。


为了更好地演示理解这个应用,下边我将通过一个实际案例来进行介绍:


首先是服务端代码:


public class RpcServer {
    public void export(Object service,int port){
        if(service==null || port<0 || port>65535){
            throw new RuntimeException("param is error");
        }
        try {
            ServerSocket serverSocket = new ServerSocket(port);
            while (true){
                final Socket socket = serverSocket.accept();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                            String methodName = objectInputStream.readUTF();
                            Class<?>[] parameterTypes = (Class<?>[]) objectInputStream.readObject();
                            Object[] args = (Object[]) objectInputStream.readObject();
                            Method method = service.getClass().getMethod(methodName,parameterTypes);
                            Object result = method.invoke(service,args);
                            output.writeObject(result);
                        } catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
复制代码


客户端代码


public class RpcClient {
    public <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
        if(interfaceClass==null){
            throw new RuntimeException("interfaceClass is null");
        }else if (host==null){
            throw new RuntimeException("host is null");
        }else if (port<0 || port>65535){
            throw new RuntimeException("port is invalid ");
        }
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket(host,port);
                OutputStream outputStream = socket.getOutputStream();
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
                objectOutputStream.writeUTF(method.getName());
                objectOutputStream.writeObject(method.getParameterTypes());
                objectOutputStream.writeObject(args);
                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                try {
                    Object result = input.readObject();
                    if (result instanceof Throwable) {
                        throw (Throwable) result;
                    }
                    return result;
                } finally {
                    input.close();
                }
            }
        });
    }
}
复制代码


接着是一些测试使用的模拟服务,代码如下所示:


public interface IUserService {
    String test();
}
public class UserServiceImpl implements  IUserService {
    @Override
    public String test() {
        System.out.println("this is test");
        return "success";
    }
}
复制代码


借助了使用代理模式设计的服务调用方和服务提供方,这里通过建立了两端的demo案例进行模拟:


首先是服务端代码:


public class ServerDemo {
    public static void main(String[] args) {
        RpcServer rpcServer = new RpcServer();
        IUserService userService = new UserServiceImpl();
        rpcServer.export(userService,9090);
    }
}
复制代码


接着是客户端代码:


public class ClientDemo {
    public static void main(String[] args) throws Exception {
        RpcClient rpcClient = new RpcClient();
        IUserService iUserService = rpcClient.refer(IUserService.class,"localhost",9090);
        iUserService.test();
    }
}
复制代码


本文总结


1.适配器模式是用来做适配,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。而且在对于系统维护的时候,适配器模式还可以作为一种用于补偿机制的设计模式来供开发者选用。


2.代理模式主要是在不改变原有类设计的基础上边通过引入相应的代理类来给原始类做扩展功能。常见的代理有划分为静态代理和动态代理两类。但是由于静态代理的可扩展性不好,因此实际工作中更多的场景会考虑使用动态代理的设计思路。比较常见的动态代理实现技术有cglib和jdk两类技术。然而使用JDK实现的动态代理不能完成继承式的动态代理,如果遇到这样的场景,可以使用cglib来实现继承式的动态代理。


3.适配器模式和代理模式两者都有点”包装(Wrapper)“数据的味道,其实这也是他们之间的一些共同性。如果要用他们的共性来划分,其实这两类设计模式可以统一称呼为结构型设计模式

目录
相关文章
|
3月前
|
设计模式
设计模式的基础问题之代理模式在工作中的问题如何解决
设计模式的基础问题之代理模式在工作中的问题如何解决
|
5月前
|
设计模式 C#
技术经验分享:C#设计模式
技术经验分享:C#设计模式
26 0
|
6月前
|
设计模式 缓存 算法
谈谈我工作中的23个设计模式
从基础的角度看,设计模式是研究类本身或者类与类之间的协作模式,是进行抽象归纳的一个很好的速成思路。后面阅读设计模式后,为了加深理解,对相关图片进行了描绘和微调。 从技术的角度已经有很多好的总结,本文会换一种角度思考,既然设计模式研究的是类与类的关系,我们作为工作的个体,一些工作中的策略是不是也可以进行类比,可以更好地去思考这些模式?答案是肯定的。
|
6月前
|
设计模式 传感器 数据处理
探索设计模式的魅力:为什么你应该了解装饰器模式-代码优化与重构的秘诀
装饰器模式是一种设计模式,它允许在运行时向对象添加额外的职责,而无需修改其代码。这种模式提供了一种动态扩展对象功能的方法,同时保持了对象的单一职责原则。本文介绍了装饰器模式的基本概念、原理、优势、适用场景、实现方法、最佳实践和注意事项。通过装饰器模式,可以将多个行为组合成一个更复杂的行为,而无需使用继承或大量的接口实现。装饰器模式适用于需要对一个对象进行一系列的增强处理的情况,而这些增强处理可以以一种松耦合的方式进行组合。通过使用装饰器模式,可以提高代码的可维护性、可扩展性和灵活性,使系统更加灵活和易于维护
117 1
探索设计模式的魅力:为什么你应该了解装饰器模式-代码优化与重构的秘诀
|
设计模式 算法 前端开发
软件开发常见的一些设计模式,留着供自己研究和面试使用
说到软件开发,就不得不提到设计模式,比如大家基本上都用过什么MVC框架开发各种系统,一些好的设计模式不仅能让软件运行的更为流畅,更能让开发人员的工作效率大大提高。本文就来列举一些常用的设计模式,供大家参考收藏。
127 1
|
设计模式 Java 程序员
设计模式 - 基本功的重要性
设计模式 - 基本功的重要性
94 0
|
设计模式 安全 Java
终于有人将23种设计模式与七大设计原则整理明白了(一)!!!
这篇文章主要介绍23种设计模式以及七大设计原则
终于有人将23种设计模式与七大设计原则整理明白了(一)!!!
|
设计模式 Java 程序员
为什么你总是觉得设计模式很难?
关于设计模式的书不少,网上博客也有很多。那我会想,如果我要写一篇关于设计模式的文章,它给读者带来的价值在哪里呢?如果只是机械地介绍一遍所有的设计模式,那其实没有多大意义。授人以鱼不如授人以渔,今天这篇文章我想探讨更多的,是「如何去认知和学习设计模式」,以及如何才能够真正让它为你所用,成为你编程的神兵利器,披荆斩棘。
530 0
|
设计模式 缓存 算法
终于有人将23种设计模式与七大设计原则整理明白了(二)!!!
这篇文章主要介绍23种设计模式以及七大设计原则
终于有人将23种设计模式与七大设计原则整理明白了(二)!!!
|
设计模式 SQL 算法
快速梳理常用的设计模式(中篇)
本文旨在快速梳理常用的设计模式,了解每个模式主要针对的是哪些情况以及其基础特征,每个模式前都有列举出一个或多个可以深入阅读的参考网页,以供读者详细了解其实现。 分为三篇文章: 上篇:设计模式基础理念和创建型设计模式 中篇:行为型设计模式 下篇:结构型设计模式
210 0