后端技术面试38讲
李智慧
同程艺龙交通首席架构师,前Intel&阿里架构师,《大型网站技术架构》作者
立即订阅
3770 人已学习
课程目录
已更新 19 讲 / 共 38 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 掌握软件开发技术的第一性原理
免费
软件的基础原理 (8讲)
01丨程序运行原理:程序是如何运行又是如何崩溃的?
02丨数据结构原理:Hash表的时间复杂度为什么是O(1)?
03丨Java虚拟机原理:JVM为什么被称为机器(machine)?
04丨网络编程原理:一个字符的互联网之旅
05丨文件系统原理:如何用1分钟遍历一个100TB的文件?
06丨数据库原理:为什么PrepareStatement性能更好更安全?
07丨编程语言原理:面向对象编程是编程的终极形态吗?
答疑丨Java Web程序的运行时环境到底是怎样的?
软件的设计原理 (9讲)
08丨软件设计的方法论:软件为什么要建模?
09丨软件设计实践:如何使用UML完成一个设计文档?
10 | 软件设计的目的:糟糕的程序员比优秀的程序员差在哪里?
11丨软件设计的开闭原则:如何不修改代码却能实现需求变更?
12 | 软件设计的依赖倒置原则:如何不依赖代码却可以复用它的功能?
13丨软件设计的里氏替换原则:正方形可以继承长方形吗?
14 | 软件设计的单一职责原则:为什么说一个类文件打开最好不要超过一屏?
15丨软件设计的接口隔离原则:如何对类的调用者隐藏类的公有方法?
16 | 设计模式基础:不会灵活应用设计模式,你就没有掌握面向对象编程
不定期加餐 (1讲)
加餐 | 软件设计文档示例模板
后端技术面试38讲
登录|注册

14 | 软件设计的单一职责原则:为什么说一个类文件打开最好不要超过一屏?

李智慧 2019-12-23
我在 Intel 工作期间,曾经接手过一个大数据 SQL 引擎的开发工作(如何自己开发一个大数据 SQL 引擎?)。我接手的时候,这个项目已经完成了早期的技术验证和架构设计,能够处理较为简单的标准 SQL 语句。后续公司打算成立一个专门的小组,开发支持完整的标准 SQL 语法的大数据引擎,然后进一步将这个产品商业化。
我接手后打开项目一看,吓出一身冷汗,这个项目只有几个类组成,其中最大的一个类,负责 SQL 语法的处理,有近万行代码。代码中充斥着大量的 switch/case,if/else 代码,而且方法之间互相调用,各种全局变量传递。
只有输入测试 SQL 语句的时候,在 debug 状态下才能理解每一行代码的意思。而这样的代码有 1 万行,现在只实现了不到 10% 的 SQL 语法特性。如果将 SQL 的全部语法特性都实现了,那么这个类该有多么大!逻辑有多么复杂!维护有多么困难!而且还要准备一个团队来合作开发!想想看,几个人在这样一个大文件里提交代码,想想都酸爽。
这是当时这个 SQL 语法处理类中的一个方法,而这样的方法有上百个。
/**
* Digest all Not Op and merge into subq or normal filter semantics
* After this process there should not be any NOT FB in the FB tree.
*/
private void digestNotOp(FilterBlockBase fb, FBPrepContext ctx) {
// recursively digest the not op in a top down manner
if (fb.getType() == FilterBlockBase.Type.LOGIC_NOT) {
FilterBlockBase child = fb.getOnlyChild();
FilterBlockBase newOp = null;
switch (child.getType()) {
case LOGIC_AND:
case LOGIC_OR: {
// not (a and b) -> (not a) or (not b)
newOp = (child.getType() == Type.LOGIC_AND) ? new OpORFilterBlock()
: new OpANDFilterBlock();
FilterBlockBase lhsNot = new OpNOTFilterBlock();
FilterBlockBase rhsNot = new OpNOTFilterBlock();
lhsNot.setOnlyChild(child.getLeftChild());
rhsNot.setOnlyChild(child.getRightChild());
newOp.setLeftChild(lhsNot);
newOp.setRightChild(rhsNot);
break;
}
case LOGIC_NOT:
newOp = child.getOnlyChild();
break;
case SUBQ: {
switch (((SubQFilterBlock) child).getOpType()) {
case ALL: {
((SubQFilterBlock) child).setOpType(OPType.SOMEANY);
SqlASTNode op = ((SubQFilterBlock) child).getOp();
// Note: here we directly change the original SqlASTNode
revertRelationalOp(op);
break;
}
case SOMEANY: {
((SubQFilterBlock) child).setOpType(OPType.ALL);
SqlASTNode op = ((SubQFilterBlock) child).getOp();
// Note: here we directly change the original SqlASTNode
revertRelationalOp(op);
break;
}
case RELATIONAL: {
SqlASTNode op = ((SubQFilterBlock) child).getOp();
// Note: here we directly change the original SqlASTNode
revertRelationalOp(op);
break;
}
case EXISTS:
((SubQFilterBlock) child).setOpType(OPType.NOTEXISTS);
break;
case NOTEXISTS:
((SubQFilterBlock) child).setOpType(OPType.EXISTS);
break;
case IN:
((SubQFilterBlock) child).setOpType(OPType.NOTIN);
break;
case NOTIN:
((SubQFilterBlock) child).setOpType(OPType.IN);
break;
case ISNULL:
((SubQFilterBlock) child).setOpType(OPType.ISNOTNULL);
break;
case ISNOTNULL:
((SubQFilterBlock) child).setOpType(OPType.ISNULL);
break;
default:
// should not come here
assert (false);
}
newOp = child;
break;
}
case NORMAL:
// we know all normal filters are either UnCorrelated or
// correlated, don't have both case at present
NormalFilterBlock nf = (NormalFilterBlock) child;
assert (nf.getCorrelatedFilter() == null || nf.getUnCorrelatedFilter() == null);
CorrelatedFilter cf = nf.getCorrelatedFilter();
UnCorrelatedFilter ucf = nf.getUnCorrelatedFilter();
// It's not likely to result in chaining SqlASTNode
// as any chaining NOT FB has been collapsed from top down
if (cf != null) {
cf.setRawFilterExpr(
SqlXlateUtil.revertFilter(cf.getRawFilterExpr(), false));
}
if (ucf != null) {
ucf.setRawFilterExpr(
SqlXlateUtil.revertFilter(ucf.getRawFilterExpr(), false));
}
newOp = child;
break;
default:
}
fb.getParent().replaceChildTree(fb, newOp);
}
if (fb.hasLeftChild()) {
digestNotOp(fb.getLeftChild(), ctx);
}
if (fb.hasRightChild()) {
digestNotOp(fb.getRightChild(), ctx);
}
}
我当时就觉得,我太难了。

单一职责原则

软件设计有两个基本准则:低耦合和高内聚。我在前面讲到过的设计原则和后面将要讲的设计模式大多数都是关于如何进行低耦合设计的。而内聚性主要研究组成一个模块或者类的内部元素的功能相关性。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《后端技术面试38讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(3)

  • 俊伟
    我觉得从测试的角度写代码也有助于代码逻辑结构的模块化划分。每次写之前想想写完了这段代码能不能测试。
    2019-12-23
    2
  • 张希音
    以前编码的时候喜欢写一大段逻辑,然后出现bug要进行debug的时候发现真的很痛苦。后来,把特定功能的逻辑抽出一个方法,每个方法只干一件事,刚开始感觉很麻烦,后来发现看代码的时候比以前清晰多了,有时候不用debug都能大概判断问题出在哪个具体方法中。

    作者回复: 👍

    2019-12-23
    2
  • imajinyun
    在维护前同事写的代码的时候,之前总是在其方法内部做增量,吃了几次亏后,果断写个方法把需要的业务处理好,然后勾到需要的地方,不光调试贼爽,还很清晰。老师讲的比较宏大,不知道我这个算不算😂

    作者回复: 算😁

    2019-12-23
    2
收起评论
3
返回
顶部