设计模式-代理模式与适配器模式实现代码重用以及策略模式的使用_策略设计模式 = 多态路由加适配器模式-程序员宅基地

技术标签: 设计模式  

前言:
在我写的项目代码中真的很少考虑关于耦合怎么减少这样的要求(可能是我待的公司更注重完成项目后拿到多少钱吧,呵呵)。什么是耦合? 我这里只想说说我自己对它的理解, 耦合的表现就是你代码能否被reuse,以及reuse的程度。怎么样?和没说一样吧,单单一个概念,我们真的无法从里面获取到更多有用的信息,关键是需要一个场景来重现一系列与需求冲突,随之的解决办法的提出这个过程。有了这个过程我们才会用耦合来描述这些烂代码烂成什么程度,才会有一系列解决耦合的设计模式的出现。
在代码重用方面,前人做了很多的努力,有需求就会有解决的方法,think in java这本书介绍了代码重用主要有两种的方式,第一就是组合(成员field引用一个需要重用的代码的对象),第二种就是继承(不仅仅是继承基类提供接口而子类不用再写直接调用完成新接口①这么简单哦,主要还有它的多态性)。这些方法都是帮助我们去解耦所写的代码,让我们可以重用,以及扩展。

为什么要写这篇文章
其实我觉得我写这篇文章不代表我就彻底里面的精华所在,可能现在我这水平只能看到冰山的一脚,但是我还是硬着头皮写下来,我这样做是为了在看一本书,看一段代码的时候,有点启发,想留住它,经过这两年的代码开发,我感触最深的就是不做总结,不思考,不记录,一直看看看,写写写,到头来一切重零开始。也就是说我们在自己的职业生命周期中一直重复学习-遗忘-学习。这种情况是让我很抓狂的,不管写的好,写的不好,是否误人子弟,这些都和我写这篇文章没有多大的关联性,我能做到的就是尽自己最大努力,将自己的想法描述出来

正文
前言向我们描述了这篇文章的目的:解耦代码、代码重用,以及更好的扩展性能。接下来我打算用代码来重现需求与代码冲突,然后解决的场景。请原谅我的脑洞不够大,无法用更合适的题材来叙述它们间的关系,只能展现具体的表现行为。
需求:我需要一个这样的方法,能够描述描述各自的职业
我的第一步幼稚的尝试
public class Functionality {
      /*
      * programmer的自我介绍
      */
      public void aboutMyJobAndMe(Programmer programmer) {
            //一些固定的不变算法
           System. out.println( "大家好,请允许我在这里自我介绍一下" );
           System. out.println( "我这里介绍一下我的职业" );
            //需要做的
           System. out.println( programmer.aboutMyJob());
            //一些固定不变的算法
           System. out.println( "很高兴能让大家认识我" );
     }
     
      public static void main(String[] args) {
           Functionality profession = new Functionality();
            profession.aboutMyJobAndMe( new Programmer());
     }
}

怎么样,可以满足暂时的需求了吗,因为这里只有一个Programmer职业,我这么写很正常,最后项目也照样可以满足客户的需求,公司照样可以将它上线然后拿到钱。但是这里 aboutMyJobAndMe 对 Programmer太过于依赖了,也就是说耦合性太大了,为什么这么讲? 如果我另外有一个职业Driver也需要这个方法来满足需求,这个 aboutMyJobAndMe方法里面的逻辑大部分固定算法确实也满足它,但是我就是不能去直接调用,不得不在copy?这样写出来的代码是不忍直视的

我的第二步幼稚的尝试
我想到了多态性(多态性你就得想到继承),编译器后期绑定给我们上面的代码带来了翻天覆地的变化,在解耦的上走出了一大步
为了重用这个 aboutMyJobAndMe 方法,我利用多态性做了下面的更改
public class Functionality {
      /*
      * profession的自我介绍
      */
      public void aboutMyJobAndMe (Profession profession ) {
            //一些固定的不变算法
           System. out.println( "大家好,请允许我在这里自我介绍一下" );
           System. out.println( "我这里介绍一下我的职业" );
            //需要做的
           System. out.println( profession.aboutMyJob());
            //一些固定不变的算法
           System. out.println( "很高兴能让大家认识我" );
     }
     
      public static void main(String[] args) {
           Functionality profession = new Functionality();
            profession. aboutMyJobAndMe( new Programmer());
            profession. aboutMyJobAndMe( new Driver());
     }
}

提示:这里面Programmer、Driver继承Profession这个基类

怎么样?在解耦上代码重用上可以满足了吧。这里需要给他一个命名,以后知道这种解决办法可以用一个对应的模式来描述,它就是策略模式。
策略模式:像本例子一样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法被称为策略设计模式(摘自think in java)。没多大神奇的地方吧??? 不就是一个多态的应用么?是这样吗?我看了网上的一些对策略的貌似很多就是根据具体的表现形式来描述,难理解,因为它有很多变种,但是脱离不了就是用这种多态来表现

前面两步的一些个人总结
从上面的第二种方法来看,我们确实可以实现代码的重用,但是在实际应用中是很复杂的,这也就出现了一些挑剔的看法,还是耦合的比较紧. think in java这本书的作者就指出了, public   void  aboutMyJobAndMe (Profession  profession  ) 这里面aboutMyJobAndMe 对Profession还是存在的依赖。 之所以说有依赖是因为他发现了一个更好的解决办法,让这个方法的限制变得更加的松动(用Interface)。使用继承来实现多态,本身就存在耦合,实现类需要依赖于基类,而基类里面有大量的需要提供给client Programmer使用的公共接口,我们不得不去继承。

现在的矛盾是代码重用选择继承,继承涉及多态,多态又存在着一点耦合。我们来看看这个耦合是到底怎么回事

第三步尝试修改
现在有一个与Profession无关的类,同样需要重用这个aboutMyJobAndMe方法,但是这个方法的参数只允许Profession基类以及其子类来传递给它,那么我这里就有很大的限制了如下面代码
public class Phone extends Product {
     
      public String aboutMyJob () {
            return "我的职业是让人类之间通过电话来实现的信息交流" ;
     }
     
}
public abstract class Product {
     
      public abstract String aboutMyJob() ;
     
}

描述:这里这么想:直接让Phone继承Profession不就可以了吗。有2点,第一点:但是实际上代码不允许这么做,我需要这个Product的继承。 第二点:这个类与Profession没有任何关系,总不能为了代码重用到处乱继承吧,继承简单来讲是用is-a来描述的,没有这样的关系最好少用继承。从这两点我们确实可以看到继承的局限性:只能传递它的基类以及其子类。

针对第三步做修改
这里引出了接口,接口比继承实现代码重用更加的宽松。
public interface CommonMethod {
      //提供的公用接口方法
      public  String aboutMyJob() ;
}

然后修改需要复用的代码的形参类型
public void aboutMyJobAndMe(CommonMethod commonmethod) {
            //一些固定的不变算法
           System. out.println( "大家好,请允许我在这里自我介绍一下" );
           System. out.println( "我这里介绍一下我的职业" );
            //需要做的
           System. out.println( commonmethod.aboutMyJob());
            //一些固定不变的算法
           System. out.println( "很高兴能让大家认识我" );
}

接下来我们第一种方式就是让客户端程序员遵循该接口来编写自己的类,就像下面
public class Programmer implements CommonMethod{
     
      public String aboutMyJob () {
            return "我的职业是一名java程序员" ;
     }
}

这么做的话Phone也是可以通过实现CommonMethod这个接口实现对aboutMyJobAndMe这个方法的。现在实际的情况没有这里想的这么简单,Phone已经存在了,我不能去修改它,一修改它,别的地方调用就很可能出现大问题,那么如何做呢?

第四步使用适配器和代理模式来实现代码的复用
我使用适配器模式来做,构建一个实现CommonMethod的类,为了能使它重用代码
public class UseAdapter implements CommonMethod{

     Product product;  //这里使用了一个代理模式
     
      public UseAdapter(Product product) {
            this. product = product;
     }
      public String aboutMyJob() {
            return product.aboutMyJob();
     }

}

这个适配器主要是将该对象传递到 aboutMyJobAndMe方法里面的。我们来看看具体调用吧
public class Functionality {
      /*
      * programmer的自我介绍
      */
      public void aboutMyJobAndMe(CommonMethod commonmethod) {
            //一些固定的不变算法
           System. out.println( "大家好,请允许我在这里自我介绍一下" );
           System. out.println( "我这里介绍一下我的职业" );
            //需要做的
           System. out.println( commonmethod.aboutMyJob());
            //一些固定不变的算法
           System. out.println( "很高兴能让大家认识我" );
     }
     
      public static void main(String[] args) {
           Functionality functionality = new Functionality();
            functionality.aboutMyJobAndMe( new Programmer());
            functionality.aboutMyJobAndMe( new Driver());
            //下面这段代码通过适配器成功调用了Phone,而Phone没有做任何修改
            functionality.aboutMyJobAndMe( new UseAdapter( new Phone()));
     }
}

对前面两步的个人总结:
现在我们知道了使用Iterface比继承有更好的复用性,我们上面只是对代码重用一个小方面的演示:利用多态(策略模式),来实现代码重用,在讨论过程中,发现如果用继承的方式来实现的话在更大的需求面前还是有一些耦合,后面采用了接口来实现对这个限制的松动,更实用了适配器与代理的搭配来解决老代码重用问题

篇尾谈谈自己的看法
进过上面的折腾,我讲讲自己的感受:太墨迹了。。。这在设计代码中谁知道后面的需求是什么,项目上线以后又怎么知道以后需要修改。这么设计以后用得到用不到都不知道,而且程序设计就是类与类之间的关联,没有绝对的不耦合(那就不是程序了)。所以我最后得出:这种设计对于我来说完全是一种艺术,绚丽的让我不敢直视,更像一种魔术,我想有远见的人才能完美驾驭。如同我这种小地方工作的Programmer还是理解这种思想,能用就用吧。

注释:
①:这里的接口就是提供client programmer使用的public 方法哦,不是我们耳熟能详的interface.

参考资料
think in java(中文第四版)-第九章


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sgls652709/article/details/48092195

智能推荐

如何提高程序员的键盘使用效率?(推荐vim入门的小游戏!!!)—— 高级软件工程第一次作业...-程序员宅基地

文章浏览阅读224次。一、程序员使用键盘最常用的技能有些?打字速度   快速盲打,做到完全脱离视觉判断的能力。以常用的87键小型键盘为例,在做到最基本的字母键盲打外,需要熟练记牢在按下shift后的扩展功能键位(例如‘!’、’&’、’^’等,特别需要注意的是乘号’*’、除号’\’,这两个键的位置实在有点奇怪,如果没有习惯,每次都会在敲这两个键的时候,浪费不少的时间)..._vim指法游戏

综合在线HTTP接口测试工具 - Getman-程序员宅基地

文章浏览阅读1w次。在线HTTP接口测试工具 - Getman_综合在线

C++简单的TCP/IP服务端与客户端(附完整代码)_c++ tcp-程序员宅基地

文章浏览阅读9.5k次,点赞18次,收藏95次。一.TCP服务端(一)创建一个TCP服务端可大致分为以下5个步骤: 1.初始化环境 2.创建监听套接字 3.监听套接字与IP地址及端口绑定 4.监听套接字 5.等待客户端连接1. 初始化环境WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);WSAStartup()函数的调用指明Windows Sockets..._c++ tcp

主从同步的延迟问题、原因及解决方案_主从同步延迟怎么解决-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏9次。MySQL主从延迟_主从同步延迟怎么解决

k8s快速入门_k8入门教程详解-程序员宅基地

文章浏览阅读1.6k次,点赞2次,收藏22次。minikube只是一个 K8S 集群模拟器,只有一个节点的集群,只为测试用,master 和 worker 都在一起直接用云平台 Kubernetes(阿里/腾讯)可视化搭建,只需简单几步就可以创建好一个集群。优点:安装简单,生态齐全,负载均衡器、存储等都给你配套好,简单操作就搞定裸机安装(Bare Metal)至少需要两台机器(主节点、工作节点个一台),需要自己安装 Kubernetes 组件,配置会稍微麻烦点。可以到各云厂商按时租用服务器,费用低,用完就销毁。_k8入门教程详解

Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException:异常解决-程序员宅基地

文章浏览阅读5.2k次。异常信息:Caused by: com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2022-05-26 15:36:45": not a valid representation (error: Failed to parse Date value '2022-05-26 15:36:45': Cannot par_caused by: com.fasterxml.jackson.databind.exc.invalidformatexception: cannot

随便推点

Java调用shell脚本要注意工作目录_java runtime工作目录-程序员宅基地

文章浏览阅读4.4k次。最近在一个web项目中要调用shell脚本,把当天生产的一个文件推送到另一台机器上去,脚本内容大致如下:#!/bin/bashfor i in `cat aa`dodate=`date +%Y_%m_%d`file=${date}_some.gz/bin/cp $file some.gz >> copy.log 2>&1 && /usr/bin/scp some.gz a_java runtime工作目录

IM消息ID技术专题(五):开源分布式ID生成器UidGenerator的技术实现_相对于时间基点"2016-05-20"的增量值-程序员宅基地

文章浏览阅读361次。1、引言很多人一想到IM应用开发,第一印象就是“长连接”、“socket”、“保活”、“协议”这些关键词,没错,这些确实是IM开发中肯定会涉及的技术范畴。但,当你真正开始编写第一行代码时,最现实的问题实际上是“聊天消息ID该怎么生成?”这个看似微不足道的小事情。说它看似微不足道,是因为在IM里它太平常了,处处可见它的身影。不过,虽然看似微不足道,但实际却很重要,因为它的生成算法和生成策略的..._相对于时间基点"2016-05-20"的增量值

江苏省C语言二级备考(5/20)_#include <stdio.h>#define strlen 100int fun(char s-程序员宅基地

文章浏览阅读246次。理论题802.11b 使用&发布最早 采用2.4GHz频带,传输速率能够根据应用环境以及其他传输因素从11Mbps自动降到5.5Mbps,或根据直接序列扩频技术调整到2Mbps和1Mbps,以保证设备正常稳定运行。802.11a 54Mbps802.11n 300Mbps802.11ac 1Gbps ..._#include #define strlen 100int fun(char s1[],char s2[]){ int i,

GCN过平滑问题-程序员宅基地

文章浏览阅读6.9k次,点赞13次,收藏40次。通过实验演示GCN的过平滑问题,并解释了一些解决方案。_过平滑问题

HTTP 常见错误列表-程序员宅基地

文章浏览阅读47次。HTTP 400 - 请求无效  HTTP 401.1 - 未授权:登录失败  HTTP 401.2 -未授权:服务器配置问题导致登录失败  HTTP 401.3 - ACL 禁止访问资源  HTTP 401.4 - 未授权:授权被筛选器拒绝  HTTP 401.5 - 未授权:ISAPI 或 CGI授权失败  HTTP 40...

《Spring Boot实战派》读书笔记--第2章 准备开发环境_springboot实战派第二章-程序员宅基地

文章浏览阅读590次。springboot的开发环境其实就是java的开发环境了,作者这章讲得一如既往的仔细和认真,生怕大家出错。在pom文件的讲解中,比较全面的讲解了它的作用和元素1.元素这里记录下,主要有dependenciesdependencyscope(以及scope的参数)properties:是自定义变量,这个后期会用到。plugin插件,打包时需要的重要信息。完整的pom..._springboot实战派第二章

推荐文章

热门文章

相关标签