重新认识来访者模式:从实践到本质
发布时间:2025-07-26
}
}
}
下述之中 FunctionExtractor 是 SqlBasicVisitor 的下述,并且本来版本了它的 visit(SqlCall) 原理,给予线性的命名并整理在了 functions 之中。
除了 visit(SqlCall) 皆,还可以通过 visit(SqlLiteral)(常量),visit(SqlIdentifier)(表名/九位)等等,解决问题极为有用的分析。
有人就会想,为什么 SqlParser不这样一来提供者区别于 getFunctions 等原理这样一来给予 SQL 之中的所有线性呢?在上记事的下述之中,getFunctions 可能或许极为有效率,但是 SQL 作为一个很有用的形态,getFunctions 对于极为有用的分析片中是不够轻松的,效能也是更为差的。如果必需,完全可以很比起简单地解决问题一个如上记事的 FunctionExtractor 来意味着效益。
二 动手解决问题除此以皆方基本型则我们更为进一步解决问题一个重构版的 SqlVisitor。
可先定义一个重构版的 SQL 形态。
将 select upper(name) from test where age> 20; 拆解到这个形态上等级关系如示意图:
我们这样一来在 Java 下述之中将上示意图的形态构造单单来:
SqlNode sql = new SelectNode(
new FieldsNode(Arrays.asList(
new FunctionCallExpression("upper", Arrays.asList(
new IdExpression("name")
)),
Arrays.asList("test"),
new WhereNode(Arrays.asList(new OperatorExpression(
new IdExpression("age"),
">",
new LiteralExpression("20")
);
这个类之中都有一个相同的原理,就是 accept:
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
这里就会通过template试用到 SqlVisitor 各有不同的 visit 原理上:
abstract class SqlVisitor {
abstract R visit(SelectNode selectNode);
abstract R visit(FieldsNode fieldsNode);
abstract R visit(WhereNode whereNode);
abstract R visit(IdExpression idExpression);
abstract R visit(FunctionCallExpression functionCallExpression);
abstract R visit(OperatorExpression operatorExpression);
abstract R visit(LiteralExpression literalExpression);
}
SQL 形态关的的类如下:
abstract class SqlNode {
// 用来接收除此以皆的原理
public abstract R accept(SqlVisitor sqlVisitor);
}
class SelectNode extends SqlNode {
private final FieldsNode fields;
private final List from;
private final WhereNode where;
SelectNode(FieldsNode fields, List from, WhereNode where) {
this.fields = fields;
this.from = from;
this.where = where;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
//... get 原理去掉
}
class FieldsNode extends SqlNode {
private final List fields;
FieldsNode(List fields) {
this.fields = fields;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
}
class WhereNode extends SqlNode {
private final List conditions;
WhereNode(List conditions) {
this.conditions = conditions;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
}
abstract class Expression extends SqlNode {
}
class IdExpression extends Expression {
private final String id;
protected IdExpression(String id) {
this.id = id;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
}
class FunctionCallExpression extends Expression {
private final String name;
private final List arguments;
FunctionCallExpression(String name, List arguments) {
this.name = name;
this.arguments = arguments;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
}
class LiteralExpression extends Expression {
private final String literal;
LiteralExpression(String literal) {
this.literal = literal;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
}
class OperatorExpression extends Expression {
private final Expression left;
private final String operator;
private final Expression right;
OperatorExpression(Expression left, String operator, Expression right) {
this.left = left;
this.operator = operator;
this.right = right;
}
@Override
public R accept(SqlVisitor sqlVisitor) {
return sqlVisitor.visit(this);
}
}
有的读者不就会注意到,每个类的 accept 原理的下述都是一样的,那为什么不这样一来读到在从父类 SqlNode 之中呢?如果更为进一步一下就就会挖掘出根本无法通过编译器,因为我们的 SqlVisitor 之中根本就从未提供者 visit(SqlNode),即使去掉了 visit(SqlNode),通过了编译器,程序的直通结果也是不符合预期的,因为此时所有的 visit 线程亦就会指向 visit(SqlNode),其他牵引原理就所设了。
导致这种现象的原因是,各有不同的 visit 原理互为两者之间只有参数各有不同,被称作“牵引”,而 Java 的 “牵引” 又被被称作 “编译器期template”,只就会根据 visit(this) 之中 this 在编译器时的类别最终线程哪个原理,而它在编译器时的类别就是 SqlNode,尽管它在直通时可能是各有不同的下述。
所以,我们可能往往就会听说用自适应语言读到除此以皆方基本型则就会极为比起简单,特别是赞成方基本型则匹配的线性基本型程序皆观设计语言(这在 Java 18 之中已经有较好赞成),后面我们再行回过头来用方基本型则匹配再行一解决问题下本段落的具体内容,再行来是不是比起简单了很多。
整整我们像前一样,是用到 SqlVisitor 更为进一步验证单单 SQL之中所有的线性线程。
可先解决问题一个 SqlVisitor,这个 SqlVisitor 所作的就是根据当前结点的形态以此线程 accept,最终将结果组装一起,巧遇 FunctionCallExpression 时将线性命名去掉到给定之中:
class FunctionExtractor extends SqlVisitor< List> {
@Override
List visit(SelectNode selectNode) {
List res = new ArrayList<>();
res.addAll(selectNode.getFields().accept(this));
res.addAll(selectNode.getWhere().accept(this));
return res;
}
@Override
List visit(FieldsNode fieldsNode) {
List res = new ArrayList<>();
for (Expression field : fieldsNode.getFields()) {
res.addAll(field.accept(this));
}
return res;
}
@Override
List visit(WhereNode whereNode) {
List res = new ArrayList<>();
for (Expression condition : whereNode.getConditions()) {
res.addAll(condition.accept(this));
}
return res;
}
@Override
List visit(IdExpression idExpression) {
return Collections.emptyList();
}
@Override
List visit(FunctionCallExpression functionCallExpression) {
// 得到线性命名
List res = new ArrayList<>();
res.add(functionCallExpression.getName());
for (Expression argument : functionCallExpression.getArguments()) {
res.addAll(argument.accept(this));
}
return res;
}
@Override
List visit(OperatorExpression operatorExpression) {
List res = new ArrayList<>();
res.addAll(operatorExpression.getLeft().accept(this));
res.addAll(operatorExpression.getRight().accept(this));
return res;
}
@Override
List visit(LiteralExpression literalExpression) {
return Collections.emptyList();
}
}
main 之中的下述如下:
public static void main(String[] args) {
// sql 定义
SqlNode sql = new SelectNode( //select
// concat("test-", upper(name))
new FieldsNode(Arrays.asList(
new FunctionCallExpression("concat", Arrays.asList(
new LiteralExpression("test-"),
new FunctionCallExpression(
"upper",
Arrays.asList(new IdExpression("name"))
)),
// from test
Arrays.asList("test"),
// where age> 20
new WhereNode(Arrays.asList(new OperatorExpression(
new IdExpression("age"),
">",
new LiteralExpression("20")
);
// 用到 FunctionExtractor
FunctionExtractor functionExtractor = new FunctionExtractor();
List functions = sql.accept(functionExtractor);
// [concat, upper]
System.out.println(functions);
}
以上就是常规的除此以皆方基本型则的解决问题,直觉感受上比前 Calcite 的 SqlBasicVisitor 用一起麻烦多了,我们整整就去解决问题 SqlBasicVisitor。
三 除此以皆方基本型则与方向上方基本型则在用到 Calcite 解决问题的 FunctionExtractor 之中,每次 Calcite 验证到线性就就会线程我们解决问题的 visit(SqlCall) ,称它为 listen(SqlCall) 似乎比 visit 极为合理。这也显示了除此以皆方基本型则与方向上方基本型则的这样一来联系。
在我们自己解决问题的 FunctionExtractor 之中,绝大多数下述都是在按照一定的排序给定各种形态,这是因为除此以皆方基本型则给予了用到者必须的轻松性,可以让解决问题者自行最终给定的排序,或者对不必需给定的大部分展开剪枝。
但是我们的效益 “验证单单 SQL 之中所有的线性”,非常爱护给定的排序,只要在经过“线性”时通报一下我们均可,对于这种比起简单效益,除此以皆方基本型则有点主因皆观设计,方向上方基本型则就会极为合理。
大多数用到除此以皆方基本型则的源代格式概念皆观设计就会给“常规除此以皆”提供者一个绑定解决问题,比如 Calcite 的 SqlBasicVisitor,绑定解决问题就会按照绑定的排序对 SQL 形态展开给定,而解决问题者只必需本来版本它爱护的大部分再多,这样就等同于于在除此以皆方基本型则的系统化上又解决问题了方向上方基本型则,即不出错除此以皆方基本型则的轻松性,也得到方向上方基本型则用到上的便利性。
我们给自己的解决问题也来去掉一个 SqlBasicVisitor 吧:
class SqlBasicVisitor extends SqlVisitor {
@Override
R visit(SelectNode selectNode) {
selectNode.getFields().accept(this);
selectNode.getWhere().accept(this);
return null;
}
@Override
R visit(FieldsNode fieldsNode) {
for (Expression field : fieldsNode.getFields()) {
field.accept(this);
}
return null;
}
@Override
R visit(WhereNode whereNode) {
for (Expression condition : whereNode.getConditions()) {
condition.accept(this);
}
return null;
}
@Override
R visit(IdExpression idExpression) {
return null;
}
@Override
R visit(FunctionCallExpression functionCallExpression) {
for (Expression argument : functionCallExpression.getArguments()) {
argument.accept(this);
}
return null;
}
@Override
R visit(OperatorExpression operatorExpression) {
operatorExpression.getLeft().accept(this);
operatorExpression.getRight().accept(this);
return null;
}
@Override
R visit(LiteralExpression literalExpression) {
return null;
}
}
SqlBasicVisitor 给每个形态都提供者了一个绑定的就会不见排序,用到这个类我们来解决问题第二版的 FunctionExtractor:
class FunctionExtractor2 extends SqlBasicVisitor {
private final List functions = new ArrayList<>();
@Override
Void visit(FunctionCallExpression functionCallExpression) {
functions.add(functionCallExpression.getName());
return super.visit(functionCallExpression);
}
public List getFunctions() {
return functions;
}
}
它的用到如下:
class Main {
public static void main(String[] args) {
SqlNode sql = new SelectNode(
new FieldsNode(Arrays.asList(
new FunctionCallExpression("concat", Arrays.asList(
new LiteralExpression("test-"),
new FunctionCallExpression(
"upper",
Arrays.asList(new IdExpression("name"))
)),
Arrays.asList("test"),
new WhereNode(Arrays.asList(new OperatorExpression(
new IdExpression("age"),
">",
new LiteralExpression("20")
);
FunctionExtractor2 functionExtractor = new FunctionExtractor2();
sql.accept(functionExtractor);
System.out.println(functionExtractor.getFunctions());
}
}
四 除此以皆方基本型则与义务链方基本型则ASM 也是一个提供者除此以皆方基本型则 API 的Perl,用来验证与转化 Java 类邮件,能就让的所有 Java 著名源代格式概念皆观设计都有他的不见到,Java8 的 Lambda 公基本型物理性质甚至都是通过它来解决问题的。如果只是能验证与转化 Java 类邮件,ASM 毕竟还不就会那么有名,更为重要的是它优秀的具象,它将常用的机制具象为一个个小的除此以皆来展开类,让有用的十六进制格式操控变得像搭积木一样比起简单。
也就是说必需按照如下方基本型则改动类邮件:
删减 name 一般来说 给所有一般来说去掉 @NonNull 释义但是单单于并行和模块化的角度考虑,我们想把两个步骤分别拆成法理的机制模块,而不是把下述读到在三人。在 ASM 之中,我们可以分别解决问题两个小除此以皆,然后串在三人,就转化成必须解决问题我们效益的除此以皆了。
删减 name 一般来说的除此以皆:
class DeleteFieldVisitor extends ClassVisitor {
// 删减的一般来说命名, 对于我们的效益,它就是 "name"
private final String deleteFieldName;
public DeleteFieldVisitor(ClassVisitor classVisitor, String deleteFieldName) {
super(Opcodes.ASM9, classVisitor);
this.deleteFieldName = deleteFieldName;
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if (name.equals(deleteFieldName)) {
// 依然行向沿河传递该一般来说, 对于沿河来说,就是被 "删减了"
return null;
}
// super.visitField 就会去在此期间线程沿河 Visitor 的 visitField 原理
return super.visitField(access, name, descriptor, signature, value);
}
}
给所有一般来说去掉 @NonNull 释义的除此以皆:
class AddAnnotationVisitor extends ClassVisitor {
public AddAnnotationVisitor(ClassVisitor classVisitor) {
super(Opcodes.ASM9, classVisitor);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
FieldVisitor fieldVisitor = super.visitField(access, name, descriptor, signature, value);
// 向沿河 Visitor 额皆传递一个 @NonNull 释义
fieldVisitor.visitAnnotation("javax/annotation/Nonnull", false);
return fieldVisitor;
}
}
在 main 之中我们将它们串一起用到:
public class AsmTest {
public static void main(String[] args) throws URISyntaxException, IOException {
Path clsPath = Paths.get(AsmTest.class.getResource("/visitordp/User.class").toURI());
byte[] clsBytes = Files.readAllBytes(clsPath);
// 串联 Visitor
// finalVisitor = DeleteFieldVisitor -> AddAnnotationVisitor -> ClassWriter
// ClassWriter 本身也是 ClassVisitor 的下述
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor finalVisitor = new DeleteFieldVisitor(new AddAnnotationVisitor(cw),
"name");
// ClassReader 就是被就会不见的某类
ClassReader cr = new ClassReader(clsBytes);
cr.accept(finalVisitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
byte[] bytes = cw.toByteArray();
Files.write(clsPath, bytes);
}
}
通过除此以皆方基本型则与义务链方基本型则的融合,我们依然行必需将所有的语义都读到在一个除此以皆之中,我们可以分立单单多个通用的除此以皆,通过组合他们解决问题极为多种多样的效益。
五 除此以皆方基本型则与预处理方基本型则“预处理” 可以算是“皆观设计方基本型则的皆观设计方基本型则”了,大量皆观设计方基本型则之中都有它的理想主义,诸如方向上方基本型则之中的“方向上”,“指令方基本型则”之中的“指令”,“状况方基本型则” 之中的 “状况” 事物上都可以比如说一个预处理线性。
除此以皆方基本型则之中的“除此以皆”毕竟也是一个预处理,和其他预处理方基本型则最小的各有不同是,“除此以皆方基本型则” 是一种带有“导航系统”的预处理,我们通过盛行的某类形态给解决问题者下一步预处理的“导航系统”,解决问题者根据“导航系统”最终下一步预处理的次序。
如果我想可先就会不见 fieldA,再行就会不见 fieldB,最终 fieldC,相异到除此以皆的解决问题就是:
visit(SomeObject someObject) {
someObject.fieldA.accept(this);
someObject.fieldB.accept(this);
someObject.fieldC.accept(this);
}
这在基本上之中的系统皆观设计就是 HATEOAS (Hypermedia as the Engine of Application State),HATEOAS 风格的 HTTP 接口除了就会赶回其他用户恳请的样本皆,还就会还来展开箱括其他用户下一步应该就会不见的 URL,如果将整个系统皆观设计的 API 而出名一个家的话,假如其他用户恳请后院的样本,那么接口除了赶回后院的样本皆,还就会赶回与后院相连的 "厨房",“房中”与“洗手间”的 URL:
这样认真的好处是,可以无缝地升级与更为换自然资源的 URL(因为这些 URL 都是服务端赶回的),而且开发者在不必需记事档的意味着顺着导航系统,可以思索学就会 API 的用到,解决了 API 其组织恐慌的问题。关于 HATEOAS 更为基本上的范例可以不见 How to GET a Cup of Coffee[1]。
六 基本上系统皆观设计前辨的范例可能都更为相反于源代格式系统化Perl的系统皆观设计,那么在极为极为多的系统皆观设计的系统之中,它要如何系统皆观设计呢?
1 有用的子程序形态就会不见
以前的 toB 系统皆观设计为了意味着各有不同企业的稀奇古怪的独创效益,提供者的配备机制越來越有用,配备项两者之间依然行是比起简单的正交法理的关系,而是相互子程序递归,正是除此以皆方基本型则发挥的公共场合。
钉钉批文的处理过程配备就是一个十分有用的形态:
认真过重构的批文流模型如下:
模型和处理过程配备的相异关系如下示意图:
RouteNode 除了像普通结点一样通过 next 连接下一个结点,其之中还来展开箱括的每个 condition 又是一个完整的处理过程配备(递归定义),由此可不见批文结点模型是有用的子程序形态。
除了既有形态有用皆,每个结点的配备也极为有用:
无能为力如此有用的配备,最好能通过配备验证二方来展开箱(后记事之中都简被称作 SDK)对系统皆观设计层屏蔽配备的有用性。如果 SDK 只是赶回一个示意图形态给系统皆观设计层的话,系统皆观设计层就被迫感知结点两者之间的关的联并且每次都必需执笔难以单单错的给定算法,此时除此以皆方基本型则就转化成了我们的不二之选。
除此以皆方基本型则的解决问题步法和前一样的,就不多说了,我们辨个系统皆观设计层范例:
可视化:让其他用户在不基本上直通处理过程的意味着就能看到处理过程的指派分支,有效率并行class ProcessSimulator implements ProcessConfigVisitor {
private List traces = new ArrayList<>();
@Override
public void visit(StartNode startNode) {
if (startNode.next != null) {
startNode.next.accept(this);
}
}
@Override
public void visit(RouteNode routeNode) {
// 算出单单也就是说的分支
for (CondtionNode conditionNode : routeNode.conditions) {
if (evalCondition(conditionNode.condition)) {
conditionNode.accept(this);
}
}
if (routeNode.next != null) {
routeNode.next.accept(this);
}
}
@Override
public void visit(ConditionNode conditionNode) {
if (conditionNode.next != null) {
conditionNode.next.accept(this);
}
}
@Override
public void visit(ApproveNode approveNode) {
// 记录下在可视化之中就会不见到的批文结点
traces.add(approveNode.id);
if (approveNode.next != null) {
approveNode.next.accept(this);
}
}
}
2 SDK 隔离缓冲线程
为了意味着 SDK 的纯粹性,一般 SDK 之中都不就会去线程缓冲接口,但是为了解决问题一些效益又被迫这么认真,此时我们可以将缓冲线程放入系统皆观设计层除此以皆的解决问题之中,然后盛行 SDK 之中指派关的语义。
在上头引用的处理过程可视化之中反复,前提条件算出常就会来展开箱括缓冲接口线程,比如通过插头线程一个其他用户指定接口最终处理过程分支,为了意味着处理过程配备验证 SDK 的纯粹性,不可能在 SDK 来展开箱之中展开线程的,因此就在除此以皆之中线程。
七 用到 Java18 解决问题除此以皆方基本型则回到本来的命题,用除此以皆方基本型则得到 SQL 之中所有的线性线程。下面说过,用线性基本型Lisp之中常不见于的方基本型则匹配可以极为有效率地解决问题,而最本来的 Java18 之中已经对此有比起好的赞成。
从 Java 14 开始,Java 赞成了一种本来的 Record 变量,下述如下:
// sealed 表示胶囊类别, 即 Expression 只并不需要是当前邮件之中 Num 和 Add
sealed interface Expression {
// record 网址代替 class, 用作定义 Record 变量
record Num(int value) implements Expression {}
record Add(int left, int right) implements Expression {}
}
TestRecord 一旦实例化,字段就是不可变的,并且它的 equals 和 hashCode 原理就会被备用本来版本,只要内皆的字段都等同于,它们就是等同于的:
public static void main(String[] args) {
Num n1 = new Num(2);
// n1.value = 10; 这行下述就会导致编译器不过
Num n2 = new Num(2);
// true
System.out.println(n1.equals(n2));
}
极为有效率的是,运用 Java 18 之中最本来的方基本型则匹配机制,可以拆解单单其之中的一般来说:
public int eval(Expression e) {
return switch (e) {
case Num(int value) -> value;
case Add(int left, int right) -> left + right;
};
}
我们首可先用到 Record 类别再行一定义我们的 SQL 形态:
sealed interface SqlNode {
record SelectNode(FieldsNode fields, List from, WhereNode where) implements SqlNode {}
record FieldsNode(List fields) implements SqlNode {}
record WhereNode(List conditions) implements SqlNode {}
sealed interface Expression extends SqlNode {
record IdExpression(String id) implements Expression {}
record FunctionCallExpression(String name, List arguments) implements Expression {}
record LiteralExpression(String literal) implements Expression {}
record OperatorExpression(Expression left, String operator, Expression right) implements Expression {}
}
}
然后运用方基本型则匹配,一个原理均可解决问题前的就会不见,得到所有线性线程:
public List extractFunctions(SqlNode sqlNode) {
return switch (sqlNode) {
case SelectNode(FieldsNode fields, List from, WhereNode where) -> {
List res = new ArrayList<>();
res.addAll(extractFunctions(fields));
res.addAll(extractFunctions(where));
return res;
}
case FieldsNode(List fields) -> {
List res = new ArrayList<>();
for (Expression field : fields) {
res.addAll(extractFunctions(field));
}
return res;
}
case WhereNode(List conditions) -> {
List res = new ArrayList<>();
for (Expression condition : conditions) {
res.addAll(extractFunctions(condition));
}
return res;
}
case IdExpression(String id) -> Collections.emptyList();
case FunctionCallExpression(String name, List arguments) -> {
// 得到线性命名
List res = new ArrayList<>();
res.add(name);
for (Expression argument : arguments) {
res.addAll(extractFunctions(argument));
}
return res;
}
case LiteralExpression(String literal) -> Collections.emptyList();
case OperatorExpression(Expression left, String operator, Expression right) -> {
List res = new ArrayList<>();
res.addAll(extractFunctions(left));
res.addAll(extractFunctions(right));
return res;
}
}
}
对比一下第二段落的下述,最小的区别就是 sqlNode.accept(visitor) 被变为了对 extractFunctions 的递归线程。另皆就是原本通过类来封装的使用暴力,转化成了极为轻量的线性。我们将在下一段落探讨其极为深入的含义。
八 再行一了解到除此以皆方基本型则在 GoF 的皆观设计方基本型则原著之中,对除此以皆方基本型则的描述如下:
表示一个作用作某某类形态之中的各要素的操控。它使你可以在不改变各要素的类的前提下定义作用作这些要素的本来操控。从这句话可以看单单,除此以皆方基本型则解决问题的所有机制事物上都可以通过给每个某类上升本来的小团体原理解决问题,运用面向某类template的物理性质,从父形态线程并且裂解子形态相应原理的赶回结果,以前的需用 SQL 所有线性为例,这一次不用除此以皆解决问题,而是在每个类之中上升一个 extractFunctions 小团体原理:
class SelectNode extends SqlNode {
private final FieldsNode fields;
private final List from;
private final WhereNode where;
SelectNode(FieldsNode fields, List from, WhereNode where) {
this.fields = fields;
this.from = from;
this.where = where;
}
public FieldsNode getFields() {
return fields;
}
public List getFrom() {
return from;
}
public WhereNode getWhere() {
return where;
}
public List extractFunctions() {
List res = new ArrayList<>();
// 在此期间线程子形态的 extractFunctions
res.addAll(fields.extractFunctions());
res.addAll(selectNode.extractFunctions());
return res;
}
}
除此以皆方基本型则事物上就是将有用的类等级形态之中小团体原理全部都具象到一个类之中去:
这两种执笔方基本型则有什么区别呢?Visitor 这个姓氏虽然看一起像名词,但是从下面的范例和论述来看,它的解决问题类全部是关于操控的具象,从方基本型则匹配的解决问题方基本型则之中就更为能看单单这一点,ASM 之中甚至将 Visitor 作为一个个小操控的具象展开排列组合,因此两种执笔方基本型则也相异两种世界观:
面向某类:认为操控必需和样本绑定到三人,即作为每个类的小团体原理存在,而不是单独需用单单来成为一个除此以皆 线性基本型编程:将样本和操控分离,将简而言之展开排列组合成为极为有用的操控,而一个除此以皆的解决问题就相异一个操控这两个方基本型则,在执笔的时候看一起区别很小,只有当必需去掉改动机制的时候才能显现单单他们的有过之而无不及,也就是说以前我们要给每个类上升一个本来操控:
这种片中看一起是上升除此以皆极为有效率。那么再行看下一个片中,也就是说以前要在类等级形态之中上升一个本来类:
这两个片中相异了硬件的两种分立方基本型则,一种是按照样本分立,一种是按照机制点分立,以阿里双十一的各个乌镇与机制为例:盒马,饿了么和聚划算分别作为一个乌镇参与了双十一的广告宣传,他们都必需提供者广告宣传,订单和支付等机制。
虽然在其他用户毕竟 盒马,饿了么和聚划算是三个各有不同的系统皆观设计,但是中下层的系统可以有两种区分方基本型则:
任何一种区分方基本型则都要忍受该种方基本型则带来的缺点。所有真实世界之中的系统皆观设计,不论是架构还是编格式,都从未上头的范例那么极端,而是两种同义。比如 盒马,饿了么,聚划算 都可以在拥有自己的系统的同时,并行广告宣传这样的按机制区分的的系统。相异到编格式也是一样,硬件工程从未银弹,我们也要根据物理性质和片中最终是有别于面向某类的具象,还是除此以皆的具象。更为多的时候必需两者同义,将大部分内部原理作为某类小团体,运用除此以皆方基本型则解决问题系统皆观设计层的那些琐碎诡异的效益。
本记事为阿里云原创具体内容,未经并不需要不得登载。
。-
往后余生,不打扰,不联系,淡淡生活,便老去
往后光阴,不想到,不联系,淡淡境遇,慢慢老去。 秋天,总有一些人送去石榴。 我的执著是一个荒地的秋天。 满满的笑容伸手在我掌心。 p