七种方法增强代码可扩展性(多图详解)

这篇具有很好参考价值的文章主要介绍了七种方法增强代码可扩展性(多图详解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


1 六大原则

在设计模式中有六大设计原则:

单一职责原则:一个类只做一件事

里式替换原则:子类可以扩展父类

依赖倒置原则:面向接口编程

接口隔离原则:高内聚低耦合

迪米特法则:最少知道原则

开闭原则:关闭修改,开放新增

我认为在这六个原则中开闭原则最为重要,开闭原则是可扩展性的重要基石。

第一个原因是需求变化时应该通过新增而不是修改已有代码实现,这样保证了代码稳定性,避免牵一发而动全身。

第二个原因是可以事先定义代码框架,扩展也是根据框架扩展,体现了用抽象构建框架,用实现扩展细节,保证了稳定性也保证了灵活性。

第三个原因是其它五个原则虽然侧重点各有不同,但是都可以包含于开闭原则。

第四个原因是标准二十三种设计模式最终都是在遵循开闭原则。

既然开闭原则如此重要,我们应该怎么在系统设计时遵循开闭原则呢?


2 数据库维度

2.1 设计类型字段

数据表设计时新增两列:biz_type、biz_sub_type,这两个列即使目前用不上,但是后续扩展可以派上大用场,可以先设置一个默认值。

biz_type可以指业务类型,业务渠道、租户等等,归根结底是用来隔离大业务类型,假设后续业务新增了一个大业务类型,那么可以通过这个字段隔离。

biz_sub_type可以在大业务类型中细分小业务,使得业务更加细化,灵活性更好。


2.2 设计扩展字段

数据表设计时新增三列:extend1、extend2、extend3,可以先设置为空。扩展字段存储JSON类型信息,存放一些扩展信息、附加信息、松散信息或者是之前未预估到的信息,保证了灵活性。

之所以设置三个扩展字段是为了增加隔离性,例如extend1存放订单扩展,extend2存放商品扩展,extend3存放营销扩展。


2.3 设计业务二进制字段

2.3.1 需求背景

数据表设计时新增业务二进制,这个字段可以很大程度上扩展业务表意能力。假设在系统中用户一共有三种角色:普通用户、管理员、超级管理员,现在需要设计一张用户角色表记录这类信息。我们不难设计出如下方案:


id name super admin normal
101 用户一 1 0 0
102 用户二 0 1 0
103 用户三 0 0 1
104 用户四 1 1 1

观察上表不难得出,用户一具有超级管理员角色,用户二具有管理员角色,用户三具有普通用户角色,用户四同时具有三种角色。如果此时新增加一种角色呢?那么新增一个字段即可。


2.3.2 发现问题

按照上述一个字段表示一种角色进行表设计功能上是没有问题的,优点是容易理解结构清晰,但是我们想一想有没有什么问题?笔者遇到过如下问题:在复杂业务环境一份数据可能会使用在不同的场景,例如上述数据存储在MySQL数据库,这一份数据还会被用在如下场景:

检索数据需要同步一份到ES

业务方使用此表通过Flink计算业务指标

业务方订阅此表Binlog消息进行业务处理

如果表结构发生变化,数据源之间就要重新进行对接,业务方也要进行代码修改,这样开发成本比较非常高。有没有办法避免此类问题?


2.3.3 解决方案

我们可以使用位图法,这样同一个字段可以表示多个业务含义。首先设计如下数据表,userFlag字段暂时不填。

id name user_flag
101 用户一 暂时不填
102 用户二 暂时不填
103 用户三 暂时不填
104 用户四 暂时不填

设计位图每一个bit表示一种角色:


七种方法增强代码可扩展性(多图详解)


使用位图法表示如下数据表:


id name super admin normal
101 用户一 1 0 0
102 用户二 0 1 0
103 用户三 0 0 1
104 用户四 1 1 1

用户一位图如下其十进制数值等于4:


七种方法增强代码可扩展性(多图详解)


用户二位图如下其十进制数值等于2:


七种方法增强代码可扩展性(多图详解)


用户三位图如下其十进制数值等于1:


七种方法增强代码可扩展性(多图详解)


用户四位图如下其十进制数值等于7:


七种方法增强代码可扩展性(多图详解)


现在我们可以填写数据表第三列:


id name user_flag
101 用户一 4
102 用户二 2
103 用户三 1
104 用户四 7

2.3.4 代码实例

定义枚举时不要直接定义为1、2、4这类数字,而应该采用位移方式进行定义,这样使用者可以明白设计者的意图。

/**
 * 用户角色枚举
 *
 * @author 微信公众号「JAVA前线」
 *
 */
public enum UserRoleEnum {

    // 1 -> 00000001
    NORMAL(1, "普通用户"),

    // 2 -> 00000010
    MANAGER(1 << 1, "管理员"),

    // 4 -> 00000100
    SUPER(1 << 2, "超级管理员")

    ;

    private int code;
    private String description;

    private UserRoleEnum(Integer code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public int getCode() {
        return this.code;
    }
}

假设用户已经具有普通用户角色,我们需要为其增加管理员角色,这就是新增角色,与之对应还有删除角色和查询角色,这些操作需要用到为位运算,详见代码注释。

/**
 * 用户角色枚举
 *
 * @author 微信公众号「JAVA前线」
 *
 */
public enum UserRoleEnum {

    // 1 -> 00000001
    NORMAL(1, "普通用户"),

    // 2 -> 00000010
    MANAGER(1 << 1, "管理员"),

    // 4 -> 00000100
    SUPER(1 << 2, "超级管理员")

    ;

    // 新增角色 -> 位或操作
    // oldRole -> 00000001 -> 普通用户
    // addRole -> 00000010 -> 新增管理员
    // newRole -> 00000011 -> 普通用户和管理员
    public static Integer addRole(Integer oldRole, Integer addRole) {
        return oldRole | addRole;
    }

    // 删除角色 -> 位异或操作
    // oldRole -> 00000011 -> 普通用户和管理员
    // delRole -> 00000010 -> 删除管理员
    // newRole -> 00000001 -> 普通用户
    public static Integer removeRole(Integer oldRole, Integer delRole) {
        return oldRole ^ delRole;
    }

    // 是否有某种角色 -> 位与操作
    // allRole -> 00000011 -> 普通用户和管理员
    // qryRole -> 00000001 -> 是否有管理员角色
    // resRole -> 00000001 -> 有普通用户角色
    public static boolean hasRole(Integer allRole, Integer qryRole) {
        return qryRole == (role & qryRole);
    }

    private int code;
    private String description;

    private UserRoleEnum(Integer code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public int getCode() {
        return this.code;
    }

    public static void main(String[] args) {
        System.out.println(addRole(1, 2));
        System.out.println(removeRole(3, 1));
        System.out.println(hasRole(3, 1));
    }
}

假设在运营后台查询界面中,需要查询具有普通用户角色的用户数据,可以使用如下SQL语句:

select * from user_role where (user_flag & 1) = user_flag;
select * from user_role where (user_flag & b'0001') = user_flag;

我们也可以使用MyBatis语句:

<select id="selectByUserRole" resultMap="BaseResultMap" parameterType="java.util.Map">
  select * from user_role 
  where user_flag & #{userFlag} = #{userFlag}
</select>

<select id="selectByUserIdAndRole" resultMap="BaseResultMap" parameterType="java.util.Map">
  select * from user_role 
  where id = #{userId} and user_flag & #{userFlag} = #{userFlag}
</select>

3 接口维度

3.1 设计类型入参

接口输入参数一定要设计类型入参,可以与数据库biz_type、biz_sub_type相对应,也可以自定义类型最终翻译为与数据库类型相对应,

如果一开始不用区分类型可以设置为默认值,但是其核心思想是一定要可以通过类型对输入参数进行区分。

public class OrderDTO {
    private Integer bizType;
    private Integer bizSubType;
    private Long amount;
    private String goodsId;
}

public Response<String> createOrder(OrderDTO order);

3.2 设计松散入参

接口输入参数可以设计Map类型松散参数,松散参数缺点是表意能力弱,优点是灵活性强,可以以较低成本新增参数。

public class OrderDTO {
    private Integer bizType;
    private Integer bizSubType;
    private Long amount;
    private String goodsId;
    private Map<String, String> params;
}

public Response<String> createOrder(OrderDTO order);

3.3 设计接口版本号

对外接口需要设计版本号,对于同一个接口,允许外部业务逐渐切到新版本,新老版本接口会共存一段时间,通过版本号区分。

/order/1.0/createOrder
/order/1.1/createOrder

3.4 纵横做设计

我们分析一个下单场景。当前有ABC三种订单类型:A订单价格9折,物流最大重量不能超过9公斤,不支持退款。B订单价格8折,物流最大重量不能超过8公斤,支持退款。C订单价格7折,物流最大重量不能超过7公斤,支持退款。按照需求字面含义平铺直叙地写代码也并不难:

public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Override
    public void createOrder(OrderBO orderBO) {
        if (null == orderBO) {
            throw new RuntimeException("参数异常");
        }
        if (OrderTypeEnum.isNotValid(orderBO.getType())) {
            throw new RuntimeException("参数异常");
        }
        // A类型订单
        if (OrderTypeEnum.A_TYPE.getCode().equals(orderBO.getType())) {
            orderBO.setPrice(orderBO.getPrice() * 0.9);
            if (orderBO.getWeight() > 9) {
                throw new RuntimeException("超过物流最大重量");
            }
            orderBO.setRefundSupport(Boolean.FALSE);
        }
        // B类型订单
        else if (OrderTypeEnum.B_TYPE.getCode().equals(orderBO.getType())) {
            orderBO.setPrice(orderBO.getPrice() * 0.8);
            if (orderBO.getWeight() > 8) {
                throw new RuntimeException("超过物流最大重量");
            }
            orderBO.setRefundSupport(Boolean.TRUE);
        }
        // C类型订单
        else if (OrderTypeEnum.C_TYPE.getCode().equals(orderBO.getType())) {
            orderBO.setPrice(orderBO.getPrice() * 0.7);
            if (orderBO.getWeight() > 7) {
                throw new RuntimeException("超过物流最大重量");
            }
            orderBO.setRefundSupport(Boolean.TRUE);
        }
        // 保存数据
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderBO, orderDO);
        orderMapper.insert(orderDO);
    }
}

上述代码从功能上完全可以实现业务需求,但是程序员不仅要满足功能,还需要思考代码的可维护性。如果新增一种订单类型,或者新增一个订单属性处理逻辑,那么我们就要在上述逻辑中新增代码,如果处理不慎就会影响原有逻辑。

如何改变平铺直叙的思考方式?这就要为问题分析加上纵向和横向两个维度,我选择使用分析矩阵方法,其中纵向表示策略,横向表示场景:


七种方法增强代码可扩展性(多图详解)


(1) 纵向做隔离

纵向维度表示策略,不同策略在逻辑上和业务上应该是隔离的,本实例包括优惠策略、物流策略和退款策略,策略作为抽象,不同订单类型去扩展这个抽象,策略模式非常适合这种场景。本文详细分析优惠策略,物流策略和退款策略同理。

// 优惠策略
public interface DiscountStrategy {
    public void discount(OrderBO orderBO);
}

// A类型优惠策略
@Component
public class TypeADiscountStrategy implements DiscountStrategy {

    @Override
    public void discount(OrderBO orderBO) {
        orderBO.setPrice(orderBO.getPrice() * 0.9);
    }
}

// B类型优惠策略
@Component
public class TypeBDiscountStrategy implements DiscountStrategy {

    @Override
    public void discount(OrderBO orderBO) {
        orderBO.setPrice(orderBO.getPrice() * 0.8);
    }
}

// C类型优惠策略
@Component
public class TypeCDiscountStrategy implements DiscountStrategy {

    @Override
    public void discount(OrderBO orderBO) {
        orderBO.setPrice(orderBO.getPrice() * 0.7);
    }
}

// 优惠策略工厂
@Component
public class DiscountStrategyFactory implements InitializingBean {
    private Map<String, DiscountStrategy> strategyMap = new HashMap<>();

    @Resource
    private TypeADiscountStrategy typeADiscountStrategy;
    @Resource
    private TypeBDiscountStrategy typeBDiscountStrategy;
    @Resource
    private TypeCDiscountStrategy typeCDiscountStrategy;

    public DiscountStrategy getStrategy(String type) {
        return strategyMap.get(type);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        strategyMap.put(OrderTypeEnum.A_TYPE.getCode(), typeADiscountStrategy);
        strategyMap.put(OrderTypeEnum.B_TYPE.getCode(), typeBDiscountStrategy);
        strategyMap.put(OrderTypeEnum.C_TYPE.getCode(), typeCDiscountStrategy);
    }
}

// 优惠策略执行
@Component
public class DiscountStrategyExecutor {
    private DiscountStrategyFactory discountStrategyFactory;

    public void discount(OrderBO orderBO) {
        DiscountStrategy discountStrategy = discountStrategyFactory.getStrategy(orderBO.getType());
        if (null == discountStrategy) {
            throw new RuntimeException("无优惠策略");
        }
        discountStrategy.discount(orderBO);
    }
}

(2) 横向做编排

横向维度表示场景,一种订单类型在广义上可以认为是一种业务场景,在场景中将独立的策略进行串联,模板方法设计模式适用于这种场景。

模板方法模式一般使用抽象类定义算法骨架,同时定义一些抽象方法,这些抽象方法延迟到子类实现,这样子类不仅遵守了算法骨架约定,也实现了自己的算法。既保证了规约也兼顾灵活性,这就是用抽象构建框架,用实现扩展细节。

// 创建订单服务
public interface CreateOrderService {
    public void createOrder(OrderBO orderBO);
}

// 抽象创建订单流程
public abstract class AbstractCreateOrderFlow {

    @Resource
    private OrderMapper orderMapper;

    public void createOrder(OrderBO orderBO) {
        // 参数校验
        if (null == orderBO) {
            throw new RuntimeException("参数异常");
        }
        if (OrderTypeEnum.isNotValid(orderBO.getType())) {
            throw new RuntimeException("参数异常");
        }
        // 计算优惠
        discount(orderBO);
        // 计算重量
        weighing(orderBO);
        // 退款支持
        supportRefund(orderBO);
        // 保存数据
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderBO, orderDO);
        orderMapper.insert(orderDO);
    }

    public abstract void discount(OrderBO orderBO);

    public abstract void weighing(OrderBO orderBO);

    public abstract void supportRefund(OrderBO orderBO);
}

// 实现创建订单流程
@Service
public class CreateOrderFlow extends AbstractCreateOrderFlow {

    @Resource
    private DiscountStrategyExecutor discountStrategyExecutor;
    @Resource
    private ExpressStrategyExecutor expressStrategyExecutor;
    @Resource
    private RefundStrategyExecutor refundStrategyExecutor;

    @Override
    public void discount(OrderBO orderBO) {
        discountStrategyExecutor.discount(orderBO);
    }

    @Override
    public void weighing(OrderBO orderBO) {
        expressStrategyExecutor.weighing(orderBO);
    }

    @Override
    public void supportRefund(OrderBO orderBO) {
        refundStrategyExecutor.supportRefund(orderBO);
    }
}

4 文章总结

本文介绍了设计类型字段、设计扩展字段、设计业务二进制字段、设计类型入参、设计松散入参、设计接口版本号、纵横做设计七种增加系统可扩展性具体方案,希望本文对大家有所帮助。文章来源地址https://www.toymoban.com/news/detail-453360.html


到了这里,关于七种方法增强代码可扩展性(多图详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何实施可扩展性测试?

    可扩展性测试是一种非功能性测试,它通过增加或减少特定规模的负载来验证系统、网络或组件的性能。您可以根据硬件、软件或数据库要求执行可伸缩性测试。可扩展性测试旨在确保系统能够应对用户流量、数据量、事务处理等的预计增长。 它也称为性能测试,重点是评估

    2024年02月08日
    浏览(43)
  • Elasticsearch的可扩展性与容错性

    Elasticsearch是一个分布式、实时的搜索和分析引擎,基于Lucene库开发。它可以处理大量数据,并提供快速、准确的搜索结果。Elasticsearch的可扩展性和容错性是其主要特点之一,使得它在大型企业和互联网公司中得到了广泛应用。本文将深入探讨Elasticsearch的可扩展性和容错性,

    2024年02月20日
    浏览(39)
  • 架构篇06-复杂度来源:可扩展性

    复杂度来源前面已经讲了高性能和高可用,今天来聊聊可扩展性。 可扩展性指系统为了应对将来需求变化而提供的一种扩展能力,当有新的需求出现时,系统不需要或者仅需要少量修改就可以支持,无须整个系统重构或者重建。 由于软件系统固有的多变性,新的需求总会不

    2024年01月19日
    浏览(39)
  • ETL架构设计:实现可扩展性和性能优化

    摘要:在当今的数据驱动时代,企业越来越重视数据的提取、转换和加载(ETL)过程。为了实现可扩展性和性能优化,设计一个高效的ETL架构至关重要。本文将介绍一些关键的设计原则和技术,帮助您构建一个可靠、可扩展和高性能的ETL架构。 随着数据量的不断增长和业务需

    2024年02月16日
    浏览(42)
  • 可扩展性和可靠性:区块链的特性使得它可以极大地满足应用场景的需要,但是同时也带来了新的挑战——系统的可扩展性

    作者:禅与计算机程序设计艺术 随着互联网、移动互联网和物联网技术的飞速发展,数据量呈爆炸式增长,这给传统数据库系统遇到的新问题提出了更加复杂的挑战。为了应对这一挑战,区块链技术应运而生,它是一个分布式数据库系统,它解决了容错和防篡改的问题,并且

    2024年02月08日
    浏览(41)
  • 架构09- 理解架构的模式3-性能和可扩展性

    一、缓存辅助模式:根据需要将数据从数据存储加载到缓存中,以提高读取性能和响应速度。 1、查缓存,不存在则查库,并更新缓存:应用程序首先尝试从缓存中获取所需数据,如果缓存中不存在,则从数据库中获取并更新缓存。这种方式可以提高读取性能和命中率。 2、直

    2024年01月18日
    浏览(42)
  • 大数据智能决策系统架构:决策系统性能与可扩展性

    作者:禅与计算机程序设计艺术 随着互联网、移动互联网、物联网等新型信息社会的发展,以及无数数据产生的实时、快速、准确,如何有效利用这些海量数据的力量来提高企业效益、优化管理、改善产品质量,成为当下企业面临的最大课题。本文将会从大数据智能决策系统

    2024年02月08日
    浏览(44)
  • 云计算:如何通过技术创新提高性能和可扩展性

    作者:禅与计算机程序设计艺术 云计算是由网络存储、服务器计算资源、软件服务、IT基础设施等软硬件资源组成的虚拟化技术所提供的一种能够让用户获得更高计算能力、规模经济、可靠性、弹性伸缩等特性的服务模型。目前,云计算已经成为实现业务快速拓展、资源节省

    2024年02月14日
    浏览(38)
  • 如何通过AI技术提高家居安全系统的可扩展性

    家居安全系统是现代智能家居的重要组成部分,它可以帮助家庭用户实现多种安全功能,如门锁、门铃、窗户、摄像头等设备的监控和控制。随着人工智能技术的不断发展,家居安全系统的可扩展性也越来越重要。可扩展性意味着系统可以轻松地添加新的设备和功能,以满足

    2024年04月08日
    浏览(76)
  • 可扩展性对物联网管理系统有哪些影响?

      可扩展性对于物联网管理系统的设计和开发非常重要,它直接影响着系统的性能、可靠性和能耗等方面,是评估一个系统优劣的重要因素之一。可扩展性对物联网管理系统的影响主要体现在以下几个方面:      设备兼容性:物联网管理系统的可扩展性意味着它可以支持各

    2024年02月08日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包