1.设计模式的前奏

news2024/11/28 1:30:02

哪些维度评判代码质量的好坏?

常用的评价标准

  • 可维护性(maintainability):维护代码的成本
  • 可读性(readability)
  • 可扩展性(extensibility):码应对未来需求变化的能力
  • 灵活性(flexibility)
  • 简洁性(simplicity)
  • 可复用性(reusability)
  • 可测试性(testability)

面向对象、设计原则、设计模式、编程规范、重构

面向对象

  1. 面向对象的四大特性:封装、抽象、继承、多态
  • 封装:信息隐藏或者数据访问保护
  • 抽象:调用者在使用函数的时候,并不需要去研究函数内部的实现逻辑,只需要通过函数的命名、注释或者文档,了解其提供了什么功能,就可以直接使用了。
  • 继承:代码复用(过度继承会导致代码可读性、可维护性变差)
  • 多态:子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现
  1. 面向对象编程与面向过程编程的区别和联系
  • 面向过程:方法和数据结构的定义分开
  • 面向对象:方法和数据结构被绑定一起,定义在类中(能够应对大规模复杂程序的开发,更易复用、易扩展、易维护)
  1. 接口和抽象类的区别以及各自的应用场景
    抽象类(模板设计模式)
// 抽象类
public abstract class Logger {
    private String name;
    private boolean enabled;
    private Level minPermittedLevel;
    public Logger(String name, boolean enabled, Level minPermittedLevel) {
        this.name = name;
        this.enabled = enabled;
        this.minPermittedLevel = minPermittedLevel;
    }
    public void log(Level level, String message) {
        boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValue
        if (!loggable) return;
        doLog(level, message);
    }
    protected abstract void doLog(Level level, String message);
}
// 抽象类的子类:输出日志到文件
public class FileLogger extends Logger {
    private Writer fileWriter;

    public FileLogger(String name, boolean enabled,
                      Level minPermittedLevel, String filepath) {
        super(name, enabled, minPermittedLevel);
        this.fileWriter = new FileWriter(filepath);
    }

    @Override
    public void doLog(Level level, String mesage) {
// 格式化level和message,输出到日志文件
        fileWriter.write(...);
    }
}

// 抽象类的子类: 输出日志到消息中间件(比如kafka)
public class MessageQueueLogger extends Logger {
    private MessageQueueClient msgQueueClient;

    public MessageQueueLogger(String name, boolean enabled,
                              Level minPermittedLevel, MessageQueueClient msgQueueClient) {
        super(name, enabled, minPermittedLevel);
        this.msgQueueClient = msgQueueClient;
    }

    @Override
    protected void doLog(Level level, String mesage) {
// 格式化level和message,输出到消息中间件
        msgQueueClient.send(...);
    }
}
  • 抽象类不允许被实例化,只能被继承。
  • 抽象类可以包含属性和方法。
  • 子类继承抽象类,必须实现抽象类中的所有抽象方法。

接口

public interface Filter {
    void doFilter(RpcRequest req) throws RpcException;
}

// 接口实现类:鉴权过滤器
public class AuthencationFilter implements Filter {
    @Override
    public void doFilter(RpcRequest req) throws RpcException {
//...鉴权逻辑..
    }
}

// 接口实现类:限流过滤器
public class RateLimitFilter implements Filter {
    @Override
    public void doFilter(RpcRequest req) throws RpcException {
//...限流逻辑...
    }
}

// 过滤器使用Demo
public class Application {
    // filters.add(new AuthencationFilter());
// filters.add(new RateLimitFilter());
    private List<Filter> filters = new ArrayList<>();

    public void handleRpcRequest(RpcRequest req) {
        try {
            for (Filter filter : filters) {
                filter.doFilter(req);
            }
        } catch (RpcException e) {
// ...处理过滤结果...
        }
// ...省略其他处理逻辑...
    }
}
  • 接口不能包含属性(也就是成员变量)。
  • 接口只能声明方法,方法不能包含代码实现。
  • 类实现接口的时候,必须实现接口中声明的所有方法。
  1. 基于接口而非实现编程的设计思想
  • 越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。
    函数的命名不能暴露任何实现细节。
    封装具体的实现细节。
    为实现类定义抽象的接口。
  1. 多用组合少用继承的设计思想
  • 如果类之间的继承结构稳定(不会轻易改变),继承层次比较浅(比如,最多有两层继承关
    系),继承关系不复杂,我们就可以大胆地使用继承。反之,系统越不稳定,继承层次很深,
    继承关系复杂,我们就尽量使用组合来替代继承
  • 装饰者模式(decorator pattern)、策略模式(strategy pattern)、组合模式(composite pattern)等都使用了组合关系,而模板模式(template pattern)使用了继承关系。
  1. 面向过程的贫血模型(重 Service 轻 BO)和面向对象的充血模型(轻 Service 重 Domain)
    基于贫血模型的传统开发模式
// 接口
public class VirtualWalletController {
    // 通过构造函数或者IOC框架注入
    private VirtualWalletService virtualWalletService;

    public BigDecimal getBalance(Long walletId) { ...} //查询余额

    public void debit(Long walletId, BigDecimal amount) { ...} //出账

    public void credit(Long walletId, BigDecimal amount) { ...} //入账

    public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) { .
//省略查询transaction的接口
    }
}


public class VirtualWalletBo {//省略getter/setter/constructor方法
    private Long id;
    private Long createTime;
    private BigDecimal balance;
}
    public Enum TransactionType {
        DEBIT,
        CREDIT,
        TRANSFER;
        }

public class VirtualWalletService {
    // 通过构造函数或者IOC框架注入
    private VirtualWalletRepository walletRepo;
    private VirtualWalletTransactionRepository transactionRepo;

    public VirtualWalletBo getVirtualWallet(Long walletId) {
        VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);
        VirtualWalletBo walletBo = convert(walletEntity);
        return walletBo;
    }

    public BigDecimal getBalance(Long walletId) {
        return walletRepo.getBalance(walletId);
    }

    @Transactional
    public void debit(Long walletId, BigDecimal amount) {
        VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);
        BigDecimal balance = walletEntity.getBalance();
        if (balance.compareTo(amount) < 0) {
            throw new NoSufficientBalanceException(...);
        }
        VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransacti
        transactionEntity.setAmount(amount);
        transactionEntity.setCreateTime(System.currentTimeMillis());
        transactionEntity.setType(TransactionType.DEBIT);
        transactionEntity.setFromWalletId(walletId);
        transactionRepo.saveTransaction(transactionEntity);
        walletRepo.updateBalance(walletId, balance.subtract(amount));
    }

    @Transactional
    public void credit(Long walletId, BigDecimal amount) {
        VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransacti
        transactionEntity.setAmount(amount);
        transactionEntity.setCreateTime(System.currentTimeMillis());
        transactionEntity.setType(TransactionType.CREDIT);
        transactionEntity.setFromWalletId(walletId);
        transactionRepo.saveTransaction(transactionEntity);
        VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);
        BigDecimal balance = walletEntity.getBalance();
        walletRepo.updateBalance(walletId, balance.add(amount));
    }

    @Transactional
    public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {
        VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransacti
        transactionEntity.setAmount(amount);
        transactionEntity.setCreateTime(System.currentTimeMillis());
        transactionEntity.setType(TransactionType.TRANSFER);
        transactionEntity.setFromWalletId(fromWalletId);
        transactionEntity.setToWalletId(toWalletId);
        transactionRepo.saveTransaction(transactionEntity);
        debit(fromWalletId, amount);
        credit(toWalletId, amount);
    }
}

基于充血模型的 DDD 开发模式(钱包系统)

public class VirtualWallet { // Domain领域模型(充血模型)
    private Long id;
    private Long createTime = System.currentTimeMillis();
    private BigDecimal balance = BigDecimal.ZERO;

    public VirtualWallet(Long preAllocatedId) {
        this.id = preAllocatedId;
    }

    public BigDecimal balance() {
        return this.balance;
    }

    public void debit(BigDecimal amount) {
        if (this.balance.compareTo(amount) < 0) {
            throw new InsufficientBalanceException(...);
        }
        this.balance = this.balance.subtract(amount);
    }

    public void credit(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidAmountException(...);
        }
        this.balance = this.balance.add(amount);
    }
}

public class VirtualWalletService {
    // 通过构造函数或者IOC框架注入
    private VirtualWalletRepository walletRepo;
    private VirtualWalletTransactionRepository transactionRepo;

    public VirtualWallet getVirtualWallet(Long walletId) {
        VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);
        VirtualWallet wallet = convert(walletEntity);
        return wallet;
    }

    public BigDecimal getBalance(Long walletId) {
        return walletRepo.getBalance(walletId);
    }

    @Transactional
    public void debit(Long walletId, BigDecimal amount) {
        VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);
        VirtualWallet wallet = convert(walletEntity);
        wallet.debit(amount);
        VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransacti
        transactionEntity.setAmount(amount);
        transactionEntity.setCreateTime(System.currentTimeMillis());
        transactionEntity.setType(TransactionType.DEBIT);
        transactionEntity.setFromWalletId(walletId);
        transactionRepo.saveTransaction(transactionEntity);
        walletRepo.updateBalance(walletId, wallet.balance());
    }

    @Transactional
    public void credit(Long walletId, BigDecimal amount) {
        VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);
        VirtualWallet wallet = convert(walletEntity);
        wallet.credit(amount);
        VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransacti
        transactionEntity.setAmount(amount);
        transactionEntity.setCreateTime(System.currentTimeMillis());
        transactionEntity.setType(TransactionType.CREDIT);
        transactionEntity.setFromWalletId(walletId);
        transactionRepo.saveTransaction(transactionEntity);
        walletRepo.updateBalance(walletId, wallet.balance());
    }

    @Transactional
    public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {
//...跟基于贫血模型的传统开发模式的代码一样...
    }
}

基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比,主要区别在 Service
层。在基于充血模型的开发模式下,我们将部分原来在 Service 类中的业务逻辑移动到了一个
充血的 Domain 领域模型中
,让 Service 类的实现依赖这个 Domain 类。

在基于充血模型的 DDD 开发模式下,Service 类并不会完全移除,而是负责一些不适合放在
Domain 类中的功能。比如,负责与 Repository 层打交道、跨领域模型的业务聚合功能、幂
等事务等非功能性的工作。

如何对接口鉴权这样一个功能开发做面向对象分析?

  1. 调用方进行接口请求的时候,将 URL、AppID、密码、时间戳拼接在一起,通过加密算法
    生成 token,并且将 token、AppID、时间戳拼接在 URL 中,一并发送到微服务端。
  2. 微服务端在接收到调用方的接口请求之后,从请求中拆解出 token、AppID、时间戳。
  3. 微服务端首先检查传递过来的时间戳跟当前时间,是否在 token 失效时间窗口内。如果已
    经超过失效时间,那就算接口调用鉴权失败,拒绝接口调用请求。
  4. 如果 token 验证没有过期失效,微服务端再从自己的存储中,取出 AppID 对应的密码,通
    过同样的 token 生成算法,生成另外一个 token,与调用方传递过来的 token 进行匹配。
    如果一致,则鉴权成功,允许接口调用;否则就拒绝接口调用。

进一步变化为:

  1. 把 URL、AppID、密码、时间戳拼接为一个字符串;
  2. 对字符串通过加密算法加密生成 token;
  3. 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL;
  4. 解析 URL,得到 token、AppID、时间戳等信息;
  5. 从存储中取出 AppID 和对应的密码;
  6. 根据时间戳判断 token 是否过期失效;
  7. 验证两个 token 是否匹配;

1、2、6、7 都是跟 token 有关,负责 token 的生成、验证;(AuthToken)
3、4 都是在处理 URL,负责 URL 的拼接、解析;(Url)
5 是操作 AppID 和密码,负责从存储中读取 AppID 和密码。(CredentialStorage)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public interface ApiAuthenticator {
    void auth(String url);
    void auth(ApiRequest apiRequest);
}
public class DefaultApiAuthenticatorImpl implements ApiAuthenticator {
    private CredentialStorage credentialStorage;
    public DefaultApiAuthenticatorImpl() {
        this.credentialStorage = new MysqlCredentialStorage();
    }
    public DefaultApiAuthenticatorImpl(CredentialStorage credentialStorage) {
        this.credentialStorage = credentialStorage;
    }
    @Override
    public void auth(String url) {
        ApiRequest apiRequest = ApiRequest.buildFromUrl(url);
        auth(apiRequest);
    }
    @Override
    public void auth(ApiRequest apiRequest) {
        String appId = apiRequest.getAppId();
        String token = apiRequest.getToken();
        long timestamp = apiRequest.getTimestamp();
        String originalUrl = apiRequest.getOriginalUrl();
        AuthToken clientAuthToken = new AuthToken(token, timestamp);
        if (clientAuthToken.isExpired()) {
            throw new RuntimeException("Token is expired.");
        }
        String password = credentialStorage.getPasswordByAppId(appId);
        AuthToken serverAuthToken = AuthToken.generate(originalUrl, appId, password,
        if (!serverAuthToken.match(clientAuthToken)) {
            throw new RuntimeException("Token verfication failed.");
        }
    }
}

设计原则

  1. SOLID 原则 -SRP 单一职责原则
  2. SOLID 原则 -OCP 开闭原则(对扩展开放、对修改关闭)
// API 接口监控告警 这种情况进行扩展就需要对方法进行修改
public class Alert {
    private AlertRule rule;
    private Notification notification;

    public Alert(AlertRule rule, Notification notification) {
        this.rule = rule;
        this.notification = notification;
    }

    public void check(String api, long requestCount, long errorCount, long duration) {
        long tps = requestCount / durationOfSeconds;
        if (tps > rule.getMatchedRule(api).getMaxTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }
        if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
            notification.notify(NotificationEmergencyLevel.SEVERE, "...");
        }

    }
}

// 当每秒钟接口超时请求个数,超过某个预先设置的最大阈值时,我们也要触发告警发送通知。
代码改动


public class Alert {
    // ...省略AlertRule/Notification属性和构造函数...
// 改动一:添加参数timeoutCount
    public void check(String api, long requestCount, long errorCount, long timeoutCount) {
        long tps = requestCount / durationOfSeconds;
        if (tps > rule.getMatchedRule(api).getMaxTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }
        if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
            notification.notify(NotificationEmergencyLevel.SEVERE, "...");
        }
        // 改动二:添加接口超时处理逻辑
        long timeoutTps = timeoutCount / durationOfSeconds;
        if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }
    }
}

重构–》

public class Alert {
    private List<AlertHandler> alertHandlers = new ArrayList<>();

    public void addAlertHandler(AlertHandler alertHandler) {
        this.alertHandlers.add(alertHandler);
    }

    public void check(ApiStatInfo apiStatInfo) {
        for (AlertHandler handler : alertHandlers) {
            handler.check(apiStatInfo);
        }
    }
}

public class ApiStatInfo {//省略constructor/getter/setter方法
    private String api;
    private long requestCount;
    private long errorCount;
    private long durationOfSeconds;
}

public abstract class AlertHandler {
    protected AlertRule rule;
    protected Notification notification;

    public AlertHandler(AlertRule rule, Notification notification) {
        this.rule = rule;
        this.notification = notification;
    }

    public abstract void check(ApiStatInfo apiStatInfo);
}

public class TpsAlertHandler extends AlertHandler {
    public TpsAlertHandler(AlertRule rule, Notification notification) {
        super(rule, notification);
    }

    @Override
    public void check(ApiStatInfo apiStatInfo) {
        long tps = apiStatInfo.getRequestCount() / apiStatInfo.getDurationOfSeconds();
        if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) {
            notification.notify(NotificationEmergencyLevel.URGENCY, "...");
        }
    }
}

public class ErrorAlertHandler extends AlertHandler {
    public ErrorAlertHandler(AlertRule rule, Notification notification) {
        super(rule, notification);
    }

    @Override
    public void check(ApiStatInfo apiStatInfo) {
        if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).g
        notification.notify(NotificationEmergencyLevel.SEVERE, "...");
    }
}


public class ApplicationContext {
    private AlertRule alertRule;
    private Notification notification;
    private Alert alert;

    public void initializeBeans() {
        alertRule = new AlertRule(/*.省略参数.*/); //省略一些初始化代码
        notification = new Notification(/*.省略参数.*/); //省略一些初始化代码
        alert = new Alert();
        alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));
        alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));
    }

    public Alert getAlert() {
        return alert;
    }

    // 饿汉式单例
    private static final ApplicationContext instance = new ApplicationContext();

    private ApplicationContext() {
        initializeBeans();
    }

    public static ApplicationContext getInstance() {
        return instance;
    }
}

public class Demo {
    public static void main(String[] args) {
        ApiStatInfo apiStatInfo = new ApiStatInfo();
// ...省略设置apiStatInfo数据值的代码
        ApplicationContext.getInstance().getAlert().check(apiStatInfo);
    }
}
  1. SOLID 原则 -LSP 里式替换原则
  2. SOLID 原则 -ISP 接口隔离原则
  3. SOLID 原则 -DIP 依赖倒置原则
  4. DRY 原则、KISS 原则、YAGNI 原则、LOD 法则

设计模式

针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思
路。

编程规范

编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具
体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范。

重构

重构作为保持代码质量不下降的有效手段,利用的就是面向对象、设计原则、设计模式、编
码规范这些理论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/180389.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【keepass】密码管理软件-推荐插件和相关工具合集-keepass工作流分析(自动填充、美化界面、快速添加记录、安全增强、软件和数据库维护类)

Keepass有很多已经开源的插件&#xff0c;生态良好&#xff0c;在官网有专门的插件推荐区。安装插件的方法很简单&#xff0c;直接把下载好的插件文件放在plugins文件夹内&#xff0c;重启软件即可。下面我以几大功能推荐一些keepass插件或搭配使用的浏览器扩展&#xff0c;以求…

Coolify系列-手把手教学解决局域网局域网中的其他主机访问虚拟机以及docker服务

背景 我在windows电脑安装了一个VM&#xff0c;使用VM开启了Linux服务器&#xff0c;运行docker&#xff0c;下载服务镜像&#xff0c;然后运行服务,然后遇到了主机无法访问服务的问题。 问题排查 STEP1:首先要开启防火墙端口&#xff0c;这个我的Coolify系列其他文章有详细…

【c++】设置控制台窗口字体颜色和背景色(system和SetConsoleTextAttribute函数 )(内含超好玩的c++游戏链接)

目录 游戏推荐 研究初步 SetConsoleTextAttribute函数 原型 参数 举个栗子 最后 题外话 一篇游戏笔记。。。 游戏推荐 最近&#xff0c;在玩&#xff08;完&#xff09;一个c的控制台游戏。 啊&#xff0c;真的非常好玩。虽然是一个文字游戏&#xff0c;但有很多隐…

分享137个ASP源码,总有一款适合您

ASP源码 分享137个ASP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 137个ASP源码下载链接&#xff1a;https://pan.baidu.com/s/13nF0yADJhSBonIFUIoymPQ?pwdmsl8 提取码&#x…

【C++】位图、布隆过滤器概念与模拟实现

目录 一、位图 1.1 位图的概念 1.2 位图的使用 1.3 位图的实现 1.4 位图的应用 二、布隆过滤器 2.1 布隆过滤器 2.2 布隆过滤器的实现 2.3 布隆过滤器练习题 一、位图 1.1 位图的概念 所谓位图&#xff0c;就是用每一位来存放某种状态&#xff0c;适用于海量数据&am…

监控Python 内存使用情况和代码执行时间

我的代码的哪些部分运行时间最长、内存最多&#xff1f;我怎样才能找到需要改进的地方&#xff1f;” 在开发过程中&#xff0c;我很确定我们大多数人都会想知道这一点&#xff0c;而且通常情况下存在开发空间。在本文中总结了一些方法来监控 Python 代码的时间和内存使用情况…

【24】C语言 | 调试技巧

目录 1、调试概念&#xff1a; 2、Debug和Release的介绍 3、windows中的快捷键 4、案例一&#xff1a;求1&#xff01; 2&#xff01;3&#xff01;...n&#xff01; 5、案例二&#xff1a;下面的代码输出什么&#xff1f; 6、案例三&#xff1a;实现一个strcopy的函数 …

零入门容器云网络实战-3->Underlay网络与Overlay网络总结

本篇文章主要用于收集、整理、总结关于Underlay网络以及overlay网络相关知识点。 1、underlay网络介绍&#xff1f; 1.1、什么是underlay网络&#xff1f; Underlay网络就是&#xff1a; 传统IT基础设施网络&#xff0c;由交换机和路由器等设备组成&#xff0c;借助以太网协议…

3分钟搭建起聊天机器人需要的NoneBot2环境

创建nonebot2运行环境 官网上说这里的Python版本要高于3.8.0&#xff0c;还会有其他的依赖。 所以这里推荐大家使用虚拟环境&#xff0c;Poetry、venv、Conda&#xff0c;我这里用的是conda环境&#xff08;不同的项目依赖可能有所不同&#xff0c;所以才创建虚拟环境&#xf…

[羊城杯 2020]EasySer

目录 信息搜集 代码审计 参数扫描 信息搜集 先扫下目录 .htaccess&#xff1b;robots.txt&#xff1b;flag.php&#xff1b;index.php 在robots.txt下看到了/star1.php 进入star1.php发现出现ser.php <!-- 小胖说用个不安全的协议从我家才能进ser.php呢&#xff01; !--…

蓝桥杯刷题015——最少刷题数(二分法+前缀和)

问题描述 小蓝老师教的编程课有 N 名学生, 编号依次是 1…N 。第 i 号学生这学期刷题的数量是 Ai​ 。 对于每一名学生, 请你计算他至少还要再刷多少道题, 才能使得全班刷题比他多的学生数不超过刷题比他少的学生数。 输入格式 第一行包含一个正整数 N 。 第二行包含 N 个整数:…

学成在线项目开发技巧整理---第一部分

学成在线项目开发技巧整理---第一部分1.数据字典2.http-client远程测试插件,可以保存测试数据3.三种跨域解决4.具有多层级数据查询思路5.Mybaits分页插件原理6.根据文件后缀解析出mime-type7.大文件上传8.Spring事务什么时候会失效9.分布式文件系统MinIo10.构建独立文件系统11.…

3.3Sram和Dram

文章目录一、引子二、存储元件1.DRAM芯片&#xff08;1&#xff09;栅极电容1&#xff09;存储2&#xff09;读出&#xff08;2&#xff09;物理特性&#xff08;3&#xff09;DRAM刷新&#xff08;4&#xff09;DRAM地址线复用2.SRAM芯片&#xff08;1&#xff09;双稳态触发器…

爬虫之JS的解析

JS的解析 学习目标&#xff1a; 了解 定位js的方法了解 添加断点观察js的执行过程的方法应用 js2py获取js的方法 1 确定js的位置 对于前面人人网的案例&#xff0c;我们知道了url地址中有部分参数&#xff0c;但是参数是如何生成的呢&#xff1f; 毫无疑问&#xff0c;参数肯…

gin全解

文章目录介绍安装快速开始&#xff08;三种启动方式&#xff09;参数获取querystring参数其他不常用方法表单参数&#xff08;form参数&#xff09;其他不常用方法获取path参数参数绑定文件上传单个文件多个文件请求&#xff08;ctx.Request)响应gin.H{}字符串响应JSON/YAML/TO…

一起自学SLAM算法:8.2 Cartographer算法

连载文章&#xff0c;长期更新&#xff0c;欢迎关注&#xff1a; Gmapping代码实现相对简洁&#xff0c;非常适合初学者入门学习。但是Gmapping属于基于滤波方法的SLAM系统&#xff0c;明显的缺点是无法构建大规模的地图&#xff0c;这一点已经在第7章中讨论过了。而基于优化的…

Python爬虫之findall和lxml

Python爬虫之findall和lxml 提示&#xff1a;前言 Python爬虫之findall和lxml 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录Python爬虫之findall和lxml前言一、导入包二、设置URL和获取视频链接三、解析视频名字…

31. 实战:PyQuery获取小电视Top100详细信息(文末源码)

目录 前言 &#xff08;链接放在评论区&#xff09;&#xff08;链接放在评论区&#xff09;&#xff08;链接放在评论区&#xff09; 目的 &#xff08;链接放在评论区&#xff09;&#xff08;链接放在评论区&#xff09;&#xff08;链接放…

趣味三角——第4章——三角学迈向解析化

第4章 三角学迈向解析化(或分析化) 目录 4.1 三角学迈向解析化的过程简述 4.2 Franois Vieter对三角学解析化的贡献 “Thus the analysis of angular sections involves geometric and arithmetic secrets which hitherto have been penetrated by no one(因此&#xf…

Idea中指定xml文件失效

目录一、&#x1f407; 项目场景&#xff1a;二、&#x1f407; 问题描述三、&#x1f407; 原因分析&#xff1a;四、&#x1f407; 解决方案&#xff1a;一、&#x1f407; 项目场景&#xff1a; 最近狮子在搞一个项目&#xff0c;需要用到数据库多表查询&#xff0c;所以在…