于映雪 发表于 2025-6-9 02:13:31

软件开发原则

软件开发原则

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

模块的单一职责
该开源项目可以作为一个jar引入,其承担的职责就是对onlyoffice集成,实现对office文件的预览和编辑。在领域驱动设计中,每个领域对象和聚合根通常应该遵循单一职责原则,确保它们只负责一个明确定义的领域职责。这有助于保持领域模型的清晰性,同时也符合单一职责原则的要求。
类的单一职责
我定义了如下几个接口类,其中每个类只负责了单一的职能
存储服务接口
package org.lboot.onlyoffice.loader;

import org.lboot.onlyoffice.domain.Document;

import java.io.InputStream;

/**
* @author kindear
* office 文档存储服务
* 该服务与第三方或者本地文件系统集成
*/
public interface OfficeStoreLoader {
    /**
   * 根据文件 key 获取文件信息
   * @param fileKey
   * @return
   */
    Document readFile(String fileKey);

    /**
   * 修改文件
   * @param fileKey
   * @param stream
   * @return
   */
    boolean writeFile(String fileKey, InputStream stream);

}鉴权服务接口
package org.lboot.onlyoffice.loader;

/**
* @author kindear
* Office 鉴权信息加载
*/
public interface OfficeAuthLoader {
    /**
   * 获取当前登录用户ID
   * @return
   */
    default String getUserId(){
      return "0";
    }

    /**
   * 获取当前登录用户名称
   * @return
   */
    default String getUserName(){
      return "guest";
    }
}配置接口
package org.lboot.onlyoffice.loader;

/**
* @author kindear
* OnlyOffice配置加载
*/
public interface OfficeConfigLoader {
    /**
   * 获取客制化LOGO地址
   * @return
   */
    @Deprecated
    default String getCustomLogo(){
      return "";
    }

    /**
   * 获取默认语言
   * 默认 zh-CN 中文
   * @return
   */
    default String getLang(){
      return "zh-CN";
    }

    /**
   * 获取回调地址
   * @return
   */
    default String getCallbackUrl(){
      return "";
    }

}开闭原则

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

子类可以扩展父类的功能,但不能改变父类原有的功能
我在代码设计中加入了基于spring上下文的事件监听机制,该子类继承自父类ApplicationEvent,该实现没有改变父类可以被spring框架管理监听的特性,又拓展了新的字段属性,使得该子类可以在被spring管理监听的基础上,携带了更多参数。
package org.lboot.onlyoffice.event;

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

import java.time.Clock;

/**
* @author kindear
* office 文档编辑构建事件 传入文件ID 和 用户ID
*/
@Getter
public class OfficeEditBuildEvent extends ApplicationEvent {
    String userId;

    String fileKey;

    public OfficeEditBuildEvent(Object source, String userId, String fileKey) {
      super(source);
      this.userId = userId;
      this.fileKey = fileKey;
    }

    public OfficeEditBuildEvent(Object source, Clock clock) {
      super(source, clock);
    }
}接口依赖原则

接口仅仅提供客户端需要的行为,即所需的方法,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口
在完成配置项后,不需要用户关注底层的编辑回调,文件装载,文件信息获取如何实现,该依赖为用户提供了最简单的调用接口OfficeCtl,所有引入该依赖的,都只需要该类即可。
package org.lboot.onlyoffice.service;

import org.lboot.onlyoffice.domain.DocEditor;
import org.lboot.onlyoffice.domain.Document;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
* @author kindear
* onlyoffice 服务实现类
*/
public interface OfficeCtl {
    /**
   * 根据文件后缀 获取office 中类型
   * @param extName
   * @return 文件类型
   */
   String getDocumentType(String extName);

    /**
   * 构建远程文档访问 Document
   * @param remoteUrl
   * @return
   */
   Document buildRemoteDocument(String remoteUrl);

    /**
   * 构建文档预览 DocEditor
   * @param document
   * @return
   */
   DocEditor buildPreviewDocEditor(Document document);

    /**
   * 构建文档编辑 DocEditor
   * @param document
   * @return
   */
   DocEditor buildEditDocEditor(Document document);

    /**
   * 预览远程文件
   * @return file-temp
   */
    @Deprecated
    ModelAndView previewRemoteFile(String remoteUrl, HttpServletResponse servletResponse);

    /**
   * 预览远程文件
   * @param remoteUrl
   * @return
   */
   ModelAndView previewRemoteFile(String remoteUrl);

    /**
   * 移动端预览远程文件
   * @param remoteUrl
   * @return
   */
   ModelAndView previewRemoteFileOnMobile(String remoteUrl);

    /**
   * 嵌入式预览远程文件
   * @param remoteUrl
   * @return
   */
   ModelAndView previewRemoteFileOnEmbedded(String remoteUrl);

    /**
   * 根据文件key 预览文件
   * @param fileKey
   * @return 预览试图
   */
   ModelAndView previewFile(String fileKey);

    /**
   * 文件预览
   * @param editor
   * @return
   */
    ModelAndView previewFile(DocEditor editor);

    /**
   * 文件预览 制定预览标题
   * @param editor
   * @param title
   * @return
   */
    ModelAndView previewFile(DocEditor editor, String title);


    /**
   * 编辑远程文件
   * @param remoteUrl
   * @return
   */
    ModelAndView editRemoteFile(String remoteUrl);



    /**
   * 文件编辑
   * @param fileKey
   * @return
   */
    ModelAndView editFile(String fileKey);

    /**
   * 文件编辑
   * @param document
   * @return
   */
    ModelAndView editFile(Document document);

    /**
   * 文件编辑
   * @param editor
   * @return
   */
    ModelAndView editFile(DocEditor editor);



    /**
   * 编辑回调
   * @param params
   * @return
   */
    Object editCallback(Map<String,Object> params);

    /**
   * 将远程访问文件转化为 Pdf
   * @param remoteUrl
   * @return pdf 下载地址
   */
    String covertToPdf(String remoteUrl);

    /**
   * 将文件转化未 pdf
   * @param document
   * @return
   */
    String covertToPdf(Document document);


    /**
   * 文件生成缩略图
   * @param remoteUrl
   * @return
   */
    String generateThumbnail(String remoteUrl);

    /**
   * 文件生成缩略图
   * @param document
   * @return
   */
    String generateThumbnail(Document document);

}依赖倒置原则

高层模块不应该依赖于底层模块,它们都应该依赖于抽象接口
以OfficeCtl接口的实现举例,注入的接口全都是抽象接口,无论是基于默认的服务实现还是拓展实现,都可以注入。
@Slf4j
@Service
@AllArgsConstructor
public class OfficeCtlImpl implements OfficeCtl {
    OnlyOfficeProperties officeProps;

    OfficeConfigLoader configLoader;

    OfficeAuthLoader authLoader;

    OfficeStoreLoader storeLoader;

    @Resource
    ApplicationContext context;
   
    //...
}迪米特法则

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

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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 软件开发原则