找回密码
 立即注册
首页 业界区 业界 软件开发原则

软件开发原则

于映雪 2025-6-9 02:13:31
软件开发原则

原则介绍单一职责原则一个类或模块应该只负责一项任务或功能开闭原则软件实体(类、模块、函数等)应该对扩展开放,对修改关闭里氏替换原则子类应该能够替换其父类并且不会破坏程序的正确性接口隔离原则客户端不应该强制依赖它不需要的接口,即应该将接口拆分成更小的部分依赖倒置原则高层模块不应该依赖于底层模块,它们都应该依赖于抽象接口迪米特法则一个类应该对自己需要耦合或调用的类知道得最少(提供最简化调用接口)聚合复用原则尽量使用对象组合,而不是继承来达到复用的目的以我个人的开源项目举例,来介绍几个基本软件开发原则的基本使用
【SpringBoot集成OnlyOffice实现文档预览】
单一职责原则

模块的单一职责
该开源项目可以作为一个jar引入,其承担的职责就是对onlyoffice集成,实现对office文件的预览和编辑。在领域驱动设计中,每个领域对象和聚合根通常应该遵循单一职责原则,确保它们只负责一个明确定义的领域职责。这有助于保持领域模型的清晰性,同时也符合单一职责原则的要求。
类的单一职责
我定义了如下几个接口类,其中每个类只负责了单一的职能
存储服务接口
  1. package org.lboot.onlyoffice.loader;
  2. import org.lboot.onlyoffice.domain.Document;
  3. import java.io.InputStream;
  4. /**
  5. * @author kindear
  6. * office 文档存储服务
  7. * 该服务与第三方或者本地文件系统集成
  8. */
  9. public interface OfficeStoreLoader {
  10.     /**
  11.      * 根据文件 key 获取文件信息
  12.      * @param fileKey
  13.      * @return
  14.      */
  15.     Document readFile(String fileKey);
  16.     /**
  17.      * 修改文件
  18.      * @param fileKey
  19.      * @param stream
  20.      * @return
  21.      */
  22.     boolean writeFile(String fileKey, InputStream stream);
  23. }
复制代码
鉴权服务接口
  1. package org.lboot.onlyoffice.loader;
  2. /**
  3. * @author kindear
  4. * Office 鉴权信息加载
  5. */
  6. public interface OfficeAuthLoader {
  7.     /**
  8.      * 获取当前登录用户ID
  9.      * @return
  10.      */
  11.     default String getUserId(){
  12.         return "0";
  13.     }
  14.     /**
  15.      * 获取当前登录用户名称
  16.      * @return
  17.      */
  18.     default String getUserName(){
  19.         return "guest";
  20.     }
  21. }
复制代码
配置接口
  1. package org.lboot.onlyoffice.loader;
  2. /**
  3. * @author kindear
  4. * OnlyOffice配置加载
  5. */
  6. public interface OfficeConfigLoader {
  7.     /**
  8.      * 获取客制化LOGO地址
  9.      * @return
  10.      */
  11.     @Deprecated
  12.     default String getCustomLogo(){
  13.         return "";
  14.     }
  15.     /**
  16.      * 获取默认语言
  17.      * 默认 zh-CN 中文
  18.      * @return
  19.      */
  20.     default String getLang(){
  21.         return "zh-CN";
  22.     }
  23.     /**
  24.      * 获取回调地址
  25.      * @return
  26.      */
  27.     default String getCallbackUrl(){
  28.         return "";
  29.     }
  30. }
复制代码
开闭原则

对扩展开放,对修改关闭
将我的项目作为依赖引入后,自然而然符合对修改关闭这个特点,
集成的业务系统又可以基于上面所定义的接口,来拓展实现功能,满足对拓展开放
具体可以查看【拓展】
里氏替换原则

子类可以扩展父类的功能,但不能改变父类原有的功能
我在代码设计中加入了基于spring上下文的事件监听机制,该子类继承自父类ApplicationEvent,该实现没有改变父类可以被spring框架管理监听的特性,又拓展了新的字段属性,使得该子类可以在被spring管理监听的基础上,携带了更多参数。
  1. package org.lboot.onlyoffice.event;
  2. import lombok.Getter;
  3. import org.springframework.context.ApplicationEvent;
  4. import java.time.Clock;
  5. /**
  6. * @author kindear
  7. * office 文档编辑构建事件 传入文件ID 和 用户ID
  8. */
  9. @Getter
  10. public class OfficeEditBuildEvent extends ApplicationEvent {
  11.     String userId;
  12.     String fileKey;
  13.     public OfficeEditBuildEvent(Object source, String userId, String fileKey) {
  14.         super(source);
  15.         this.userId = userId;
  16.         this.fileKey = fileKey;
  17.     }
  18.     public OfficeEditBuildEvent(Object source, Clock clock) {
  19.         super(source, clock);
  20.     }
  21. }
复制代码
接口依赖原则

接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口
在完成配置项后,不需要用户关注底层的编辑回调,文件装载,文件信息获取如何实现,该依赖为用户提供了最简单的调用接口OfficeCtl,所有引入该依赖的,都只需要该类即可。
  1. package org.lboot.onlyoffice.service;
  2. import org.lboot.onlyoffice.domain.DocEditor;
  3. import org.lboot.onlyoffice.domain.Document;
  4. import org.springframework.web.servlet.ModelAndView;
  5. import javax.servlet.http.HttpServletResponse;
  6. import java.util.Map;
  7. /**
  8. * @author kindear
  9. * onlyoffice 服务实现类
  10. */
  11. public interface OfficeCtl {
  12.     /**
  13.      * 根据文件后缀 获取office 中类型
  14.      * @param extName
  15.      * @return 文件类型
  16.      */
  17.      String getDocumentType(String extName);
  18.     /**
  19.      * 构建远程文档访问 Document
  20.      * @param remoteUrl
  21.      * @return
  22.      */
  23.      Document buildRemoteDocument(String remoteUrl);
  24.     /**
  25.      * 构建文档预览 DocEditor
  26.      * @param document
  27.      * @return
  28.      */
  29.      DocEditor buildPreviewDocEditor(Document document);
  30.     /**
  31.      * 构建文档编辑 DocEditor
  32.      * @param document
  33.      * @return
  34.      */
  35.      DocEditor buildEditDocEditor(Document document);
  36.     /**
  37.      * 预览远程文件
  38.      * @return file-temp
  39.      */
  40.     @Deprecated
  41.     ModelAndView previewRemoteFile(String remoteUrl, HttpServletResponse servletResponse);
  42.     /**
  43.      * 预览远程文件
  44.      * @param remoteUrl
  45.      * @return
  46.      */
  47.      ModelAndView previewRemoteFile(String remoteUrl);
  48.     /**
  49.      * 移动端预览远程文件
  50.      * @param remoteUrl
  51.      * @return
  52.      */
  53.      ModelAndView previewRemoteFileOnMobile(String remoteUrl);
  54.     /**
  55.      * 嵌入式预览远程文件
  56.      * @param remoteUrl
  57.      * @return
  58.      */
  59.      ModelAndView previewRemoteFileOnEmbedded(String remoteUrl);
  60.     /**
  61.      * 根据文件key 预览文件
  62.      * @param fileKey
  63.      * @return 预览试图
  64.      */
  65.      ModelAndView previewFile(String fileKey);
  66.     /**
  67.      * 文件预览
  68.      * @param editor
  69.      * @return
  70.      */
  71.     ModelAndView previewFile(DocEditor editor);
  72.     /**
  73.      * 文件预览 制定预览标题
  74.      * @param editor
  75.      * @param title
  76.      * @return
  77.      */
  78.     ModelAndView previewFile(DocEditor editor, String title);
  79.     /**
  80.      * 编辑远程文件
  81.      * @param remoteUrl
  82.      * @return
  83.      */
  84.     ModelAndView editRemoteFile(String remoteUrl);
  85.     /**
  86.      * 文件编辑
  87.      * @param fileKey
  88.      * @return
  89.      */
  90.     ModelAndView editFile(String fileKey);
  91.     /**
  92.      * 文件编辑
  93.      * @param document
  94.      * @return
  95.      */
  96.     ModelAndView editFile(Document document);
  97.     /**
  98.      * 文件编辑
  99.      * @param editor
  100.      * @return
  101.      */
  102.     ModelAndView editFile(DocEditor editor);
  103.     /**
  104.      * 编辑回调
  105.      * @param params
  106.      * @return
  107.      */
  108.     Object editCallback(Map<String,Object> params);
  109.     /**
  110.      * 将远程访问文件转化为 Pdf
  111.      * @param remoteUrl
  112.      * @return pdf 下载地址
  113.      */
  114.     String covertToPdf(String remoteUrl);
  115.     /**
  116.      * 将文件转化未 pdf
  117.      * @param document
  118.      * @return
  119.      */
  120.     String covertToPdf(Document document);
  121.     /**
  122.      * 文件生成缩略图
  123.      * @param remoteUrl
  124.      * @return
  125.      */
  126.     String generateThumbnail(String remoteUrl);
  127.     /**
  128.      * 文件生成缩略图
  129.      * @param document
  130.      * @return
  131.      */
  132.     String generateThumbnail(Document document);
  133. }
复制代码
依赖倒置原则

高层模块不应该依赖于底层模块,它们都应该依赖于抽象接口
以OfficeCtl接口的实现举例,注入的接口全都是抽象接口,无论是基于默认的服务实现还是拓展实现,都可以注入。
  1. @Slf4j
  2. @Service
  3. @AllArgsConstructor
  4. public class OfficeCtlImpl implements OfficeCtl {
  5.     OnlyOfficeProperties officeProps;
  6.     OfficeConfigLoader configLoader;
  7.     OfficeAuthLoader authLoader;
  8.     OfficeStoreLoader storeLoader;
  9.     @Resource
  10.     ApplicationContext context;
  11.    
  12.     //...
  13. }
复制代码
迪米特法则

一个类应该对自己需要耦合或调用的类知道得最少(提供最简化调用接口)
例如读取本地存储系统文件并预览的接口
  1.         @SneakyThrows
  2.           @Override
  3.     public ModelAndView previewFile(String fileKey) {
  4.         Document document = storeLoader.readFile(fileKey);
  5.         DocEditor docEditor = buildPreviewDocEditor(document);
  6.         return previewFile(docEditor);
  7.     }
复制代码
我们只需要关注调用storeLoader.readFile(fileKey);可以获取对应的信息,对于该接口中如何获取文件并读取信息的实现不需要关注。
合成复用原则

OfficeCtl的实现类即是几种服务的合成复用的案例

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册