尺度不变性是指什么不变_不变性如何提供帮助

尺度不变性是指什么不变

在最近的几篇文章中,包括“ Getters / Setters。 邪恶。 期。” , “对象应该是不可变的”和“依赖注入容器是代码污染者” ,我普遍将所有可变对象标记为“ setter”(以set开头的对象方法)。 我的论证主要是基于隐喻和抽象实例。 显然,这对你们中的许多人来说还不够令人信服-我收到了一些要求提供更具体和实际示例的请求。

因此,为了说明我对“通过setter进行的可变性”的强烈反对态度,我从Apache那里获取了一个现有的commons-email Java库,并以不依赖setter和“对象思考”的方式重新设计了它。 我作为jcabi家族的一部分— jcabi-email发布了我的库。 让我们看看从没有吸气剂的“纯”面向对象,不变的方法中获得的好处。

如果您使用commons-email发送电子邮件,则代码外观如下:

Email email = new SimpleEmail();
email.setHostName("smtp.googlemail.com");
email.setSmtpPort(465);
email.setAuthenticator(new DefaultAuthenticator("user", "pwd"));
email.setFrom("yegor@teamed.io", "Yegor Bugayenko");
email.addTo("dude@jcabi.com");
email.setSubject("how are you?");
email.setMsg("Dude, how are you?");
email.send();

这是使用jcabi-email的相同方法:

Postman postman = new Postman.Default(new SMTP("smtp.googlemail.com", 465, "user", "pwd")
);
Envelope envelope = new Envelope.MIME(new Array<Stamp>(new StSender("Yegor Bugayenko <yegor@teamed.io>"),new StRecipient("dude@jcabi.com"),new StSubject("how are you?")),new Array<Enclosure>(new EnPlain("Dude, how are you?"))
);
postman.send(envelope);

我认为区别很明显。

在第一个示例中,您正在处理一个怪物类,该类可以为您做所有事情,包括通过SMTP发送MIME消息,创建消息,配置其参数,向其中添加MIME部分等。Common的Email类电子邮件确实是一个巨大的类-33个私有属性,一百多种方法,大约两千行代码。 首先,通过一堆设置器配置类,然后要求它为您send()电子邮件。

在第二个示例中,我们通过七个new调用实例化了七个对象。 Postman负责打包MIME邮件; SMTP负责通过SMTP发送它; 标记( StSenderStRecipientStSubject )负责在传递之前配置MIME消息; EnPlain附件负责为我们要发送的消息创建MIME部分。 我们构造了这七个对象,将它们封装在一起,然后要求邮递员为我们send()信封。

可变电子邮件有什么问题?

从用户的角度来看,几乎没有错。 Email是一门功能强大的类,具有多个控件-只需单击正确的控件即可完成工作。 但是,从开发人员的角度来看, Email类是一场噩梦。 主要是因为该课程非常大且难以维护。

因为该类非常大所以每次您想通过引入新方法来对其进行扩展时,您都面临着使该类变得更糟的事实,即更长,更缺乏凝聚力,可读性更差,更难以维护等。感觉到您正在挖掘肮脏的东西,没有希望使其变得更清洁。 我敢肯定,您对这种感觉很熟悉-大多数旧版应用程序都是这样的。 它们具有巨大的多行“类”(实际上是用Java编写的COBOL程序),这些类是从您之前的几代程序员那里继承而来的。 当您开始时,您精力充沛,但是在滚动了这样的“课堂”几分钟后,您会说-“拧紧,几乎是星期六”。

由于类很大 ,因此不再存在数据隐藏或封装的问题-超过100种方法可访问33个变量。 隐藏了什么? 实际上,此Email.java文件是一个大型的程序化2000行脚本,被误称为“类”。 一旦通过调用类的方法之一跨越了类的边界,任何东西都不会被隐藏。 之后,您就可以完全访问可能需要的所有数据。 为什么这样不好? 那么,为什么我们首先需要封装? 为了保护一个程序员免受另一种程序员的攻击,又称为防御性编程 。 当我忙于更改MIME消息的主题时,我想确保自己不会被其他方法的活动所干扰,即正在更改发件人并误触了我的主题。 封装可帮助我们缩小问题的范围,而此Email类的作用恰恰相反。

由于类太大因此它的单元测试比类本身还要复杂。 为什么? 由于其方法和属性之间存在多种相互依赖关系。 为了测试setCharset()您必须通过调用其他一些方法来准备整个对象,然后必须调用send()以确保要发送的消息实际上使用了您指定的编码。 因此,为了测试单行方法setCharset()您运行了整个集成测试方案,即通过SMTP发送完整的MIME消息。 显然,如果其中一种方法发生更改,几乎每种测试方法都会受到影响。 换句话说,测试非常脆弱,不可靠且过于复杂。

我可以继续讲这个“ 因为班级很大 ”,但是我认为很明显,一个小而有凝聚力的班级总是比大班级更好。 对于我,您和任何面向对象的程序员而言,这都是显而易见的。 但是,为什么对于Apache Commons Email的开发人员来说并不那么明显? 我不认为他们是愚蠢或没有受过教育的人。 之后怎么样了?

它是如何发生的以及为什么发生的?

这就是它总是发生的方式。 您开始将类设计为有凝聚力的,坚固的和小巧的。 您的意图非常积极。 很快,您意识到该类还有其他事情要做。 然后,还有其他事情。 然后,甚至更多。

使您的类越来越强大的最好方法是添加将设置参数注入到类中的setter,以便它可以在内部处理它们,不是吗?

这是问题的根本原因! 根本原因是我们能够通过配置方法(也称为“设置程序”) 数据插入可变对象中。 当一个对象是可变的并且允许我们在需要时添加setter时,我们将无限制地进行操作。

让我这样说吧– 可变的类倾向于增加规模并失去凝聚力

如果commons-email作者在开始时就将此Email类设为不可变的,那么他们将无法向其中添加太多方法并封装太多属性。 他们将无法将其变成怪物。 为什么? 因为不可变对象仅通过构造函数接受状态。 您能想象一个33参数的构造函数吗? 当然不是。

首先,当您使类不可变时,您必须保持其凝聚力,小巧,牢固和强大。 因为不能封装太多,也不能修改封装的内容。 只需一个构造函数的两个或三个参数就可以了。

我如何设计不可变的电子邮件?

徽标广场
在设计jcabi-email时,我从一个简单的类开始: Postman 。 好吧,这是一个接口,因为我从来没有做无接口类。 因此, Postman是…… Postman 。 他正在向其他人传递消息。 首先,我创建了它的默认版本(为简洁起见,我省略了ctor):

import javax.mail.Message;
@Immutable
class Postman.Default implements Postman {private final String host;private final int port;private final String user;private final String password;@Overridevoid send(Message msg) {// create SMTP session// create transport// transport.connect(this.host, this.port, etc.)// transport.send(msg)// transport.close();}
}

良好的开端,它有效。 现在怎么办? 好吧, Message很难构造。 它是JDK中的一个复杂类,需要进行一些操作才能成为HTML电子邮件。 因此,我创建了一个信封,它将为我构建这个复杂的对象(请注意, PostmanEnvelope都是不可变的,并在jcabi-aspects中使用@Immutable进行了注释):

@Immutable
interface Envelope {Message unwrap();
}

我还重构Postman以接受信封,而不是消息:

@Immutable
interface Postman {void send(Envelope env);
}

到目前为止,一切都很好。 现在,让我们尝试创建一个简单的Envelope实现:

@Immutable
class MIME implements Envelope {@Overridepublic Message unwrap() {return new MimeMessage(Session.getDefaultInstance(new Properties()));}
}

它可以工作,但是没有任何用处。 它只会创建一个绝对为空的MIME消息并将其返回。 如何为其添加一个主题以及“ To:和“ From:地址(请注意, MIME类也是不可变的):

@Immutable
class Envelope.MIME implements Envelope {private final String subject;private final String from;private final Array<String> to;public MIME(String subj, String sender, Iterable<String> rcpts) {this.subject = subj;this.from = sender;this.to = new Array<String>(rcpts);}@Overridepublic Message unwrap() {Message msg = new MimeMessage(Session.getDefaultInstance(new Properties()));msg.setSubject(this.subject);msg.setFrom(new InternetAddress(this.from));for (String email : this.to) {msg.setRecipient(Message.RecipientType.TO,new InternetAddress(email));}return msg;}
}

看起来正确且有效。 但这仍然太原始了。 CC:BCC:如何? 电子邮件文字呢? PDF附件怎么样? 如果我想指定消息的编码怎么办? Reply-To呢?

我可以将所有这些参数添加到构造函数中吗? 请记住,该类是不可变的,因此我无法介绍setReplyTo()方法。 我必须将replyTo参数传递给它的构造函数。 这是不可能的,因为构造函数将有太多的参数,并且没有人可以使用它。

那么,我该怎么办?

好吧,我开始思考:我们如何将“信封”的概念分解为较小的概念,这就是我所发明的。 就像现实的信封一样,我的MIME对象将带有图章。 Stamp将负责配置对象Message (同样, Stamp及其所有实现者都是不可变的):

@Immutable
interface Stamp {void attach(Message message);
}

现在,我可以将我的MIME类简化为以下内容:

@Immutable
class Envelope.MIME implements Envelope {private final Array<Stamp> stamps;public MIME(Iterable<Stamp> stmps) {this.stamps = new Array<Stamp>(stmps);}@Overridepublic Message unwrap() {Message msg = new MimeMessage(Session.getDefaultInstance(new Properties()));for (Stamp stamp : this.stamps) {stamp.attach(msg);}return msg;}
}

现在,我将为该主题创建图章, To: ,为From: ,为CC: BCC:BCC: ,等等。 MIME类将保持不变-小,内聚,可读,固定等。

这里重要的是为什么我决定在班级相对较小的时候决定进行重构。 确实,当我的MIME类只有25行时,我开始担心这些标记类。

这正是本文的重点- 不变性迫使您设计小型且具有凝聚力的对象

没有不变性,我本来会和commons-email一样。 我的MIME类的大小会增加,迟早会变得与commons-email中的Email一样大。 阻止我的唯一原因是必须对其进行重构,因为我无法通过构造函数传递所有参数。

没有不变性,我就不会有那种动力,而我会做Apache开发人员使用commons-email所做的事情—使类膨胀,并将其变成难以维护的怪物。

那是jcabi-email 。 我希望这个例子足以说明问题,并且您将开始使用不可变的对象编写更简洁的代码。

相关文章

您可能还会发现以下有趣的帖子:

  • 配对支架
  • 避免字符串串联
  • Java代码中的典型错误
  • DI容器是代码污染者
  • Getters / Setters。 邪恶。 期。

翻译自: https://www.javacodegeeks.com/2014/11/how-immutability-helps.html

尺度不变性是指什么不变

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

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

相关文章

java prototype是什么,java设计模式-原型模式(Prototype)

定义原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型&#xff0c;然后用复制这个原型对象的办法创建出更多同类型的对象。这就是原型模式的用意原型模式的结构原型模式要求对象实现同一个可以“克隆”自身的接口&#xff0c;遮掩个就可以通过赋值…

Windows 动态链接库 DLL 浅析

一、概念DLL&#xff1a;Dynamic Link Library&#xff0c;即动态链接库&#xff0c;这种库包含了可由多个程序同时使用的代码和数据。它是microsoft在windows操作系统中实现共享函数库概念的一种实现方式。其中windows中 一些作为DLL实现的文件有&#xff1a;ActiveX控件&…

图片大小 媒体大小自适应_自适应堆大小

图片大小 媒体大小自适应在改进测试平台以改进Plumbr GC问题检测器的同时 &#xff0c;我最终编写了一个小型测试用例&#xff0c;我认为这对于更广泛的读者来说可能很有趣。 我追求的目标是测试JVM在eden&#xff0c;survivor和Tenured空间之间如何分割堆方面的自适应性。 测…

如何优雅的激怒C/C++程序员

一、想知道怎么激怒C/C程序员及爱好者吗&#xff1f;那就来一起看看吧&#xff1a;C是一个编译很快&#xff0c;但运行很慢的语言。对此我不接受反驳&#xff0c;C我早就精通了。你问我需要多久才能精通&#xff0c;只要14天。前13天学C&#xff0c;最后1天看看面向对象就行。C…

java xml格式打包,maven项目打包xml没有被打包解决办法 ,mybatis的xml打包

在打maven包是遇见一个问题&#xff1a;打完包发现src/main/java中的的mapping没有被打包进去&#xff1b;原因主要是mapping目录里面的文件都是xml文件并不是.java文件&#xff0c;而maven打包默认的src/main/java的是Java文件&#xff0c;它不会打包里面的xml文件&#xff0c…

老司机找BUG指南,赶紧拿走。。

一.码畜&#xff1a;靠编译器帮自己查语法错误消灭笔误:编写适合程序员的键盘练习if (常量变量或表达式)使用goto接力超长的if,switch连续的if还是if elseif多个条件的组合:精心的排版多重括号的匹配条件编译各种const:不要纠结各种常量了&#xff0c;这个世界上唯一不变的就是…

红帽 jboss_红帽JBoss BRMS和BPMS富客户端框架展示了与GWT / Errai / UberFire和AngularJS的多语言集成...

红帽 jboss上周&#xff0c;我发布了一个博客&#xff0c;重点介绍了我的演示文稿&#xff0c;该演示文稿展示了我们在BRMS和BPMS平台内完成的工作&#xff0c;Drools和jBPM项目的产品化版本所产生的丰富客户端平台。 该演示文稿是所有屏幕截图和视频&#xff0c;您可以在此处找…

你知道自己适合做程序员吗?

哪有什么适不适合?编码本来就不是一件复杂的事情&#xff0c;日常工作不就是敲码、找Bug、 跟产品经理吵架、终于下班了、回家改BUG吗?今天看到了最奇葩的辞职理由&#xff1a;我觉得我数学不好&#xff0c;我不想做编程 我坐不住&#xff0c;我不想做编程 我性格太活泼&…

C/C++冷门知识点你知道多少呢?

在学习C语言的过程中&#xff0c;我们都自己总结了一些重要的知识点&#xff0c;但是有些不常用的冷门知识点可能就被我们选择性忘记了&#xff0c;你要不要来回一下呢&#xff1f;1&#xff1a;int跟signed int是一回事&#xff0c;short、long亦然&#xff0c;但char不是。ch…

判断人物眼型matlab,怎么判断眼型和脸型?

原标题&#xff1a;怎么判断眼型和脸型&#xff1f;为了判断脸部的形状&#xff0c;建议从面部类型诊断的长度和比例来衡量。我们只测量2个地方的长度和宽度。长度是从眉毛位置到嘴巴的长度&#xff0c;宽度是以嘴巴位置为中心到两个脸颊的宽度。脸型大致分为5种类型脸型大致分…

maven 打包编译_您是否真的想加快Maven的编译/打包速度? 那么takari生命周期插件就是答案。...

maven 打包编译像你们中的许多人一样&#xff0c;我正在使用多模块Maven项目 。 与现有的许多系统相比&#xff0c;它不是一个巨大的系统&#xff0c;它具有15个模块&#xff0c;3种不同的耳朵部署&#xff0c;带有属性文件的大量参数化以及大约10万行Java代码。 在开发高峰期&…

C/C++制作人机猜拳小游戏

C语言学完了之后总是感觉做不出什么有意思的东西&#xff0c;今天我们就来做一个好玩的小游戏试试。#include<stdio.h> #include<time.h> #include<conio.h> #include <windows.h>void win(int a, int b); void wait(); void softgame(); void lefttim…

r 数据框选子集_在带有组合框的值列表的下拉列表中显示显示属性的子集

r 数据框选子集组合框值列表&#xff08;inputComboboxListOfValues&#xff09;应该是使用LOV的非常流行的ADF Faces组件。 坦白说&#xff0c;这是我最喜欢的值列表方法。 在这篇简短的文章中&#xff0c;我将重点介绍ADF开发人员经常忽略的一项功能。 如果默认情况下定义了L…

1024到了,默默给自己点个赞!

只有程序员才能看懂的搞笑图片写了一大堆SQL语句&#xff0c;自己都不敢相信居然运行成功了找bug给客户演示&#xff0c;我的bug没被发现正则表达式返回预期效果正则表达式没有返回预期效果上线两天了&#xff0c;客户突然要改需求&#xff0c;我去年买了个表一口气写完代码&am…

input发送a.jax_Java EE 7 / JAX-RS 2.0:具有自定义HTTP标头的简单REST API身份验证和授权...

input发送a.jax在使用已可用的HTTP协议实施Web服务时&#xff0c;REST带来了很多便利。 通过仅通过指定的URL触发GET&#xff0c;POST和其他HTTP方法&#xff0c;您将确保通过REST服务的响应来完成某些工作。 但是&#xff0c;无论REST给开发人员带来了什么便利&#xff0c;安全…

C语言的main函数,究竟有几种写法?

从学习C语言开始就一直写个一个函数&#xff0c;那么你知道它的标准写法什么什么样吗&#xff1f;main函数&#xff0c;又称主函数&#xff0c;是程序执行的起点&#xff0c;我们平时写的main函数是什么样呢&#xff1f;所以说&#xff0c;其他的写法并不符合标准&#xff0c;有…

Linux C语言结构体

前面学习了c语言的基本语法特性&#xff0c;本节进行更深入的学习。预处理程序。 编译指令: 预处理, 宏定义&#xff0c;建立自己的数据类型&#xff1a;结构体&#xff0c;联合体&#xff0c;动态数据结构c语言表达式工具 逻辑运算符&#xff1a; & | ^ ~ << >&g…

Linux C语言编程基本原理与实践

重识C语言C语言是一种通用的, 面向过程的编程语言, 在系统与应用软件的开发应用较广是人类和计算机交流的一种方式ANSI C&#xff1a; 是C语言的标准, 为了避免各开发商用的C语言语法的差异C语言的特点: 简单, 快速, 高性能, 兼容性好, 功能强大, 易于学习C语言适合做什么Linux…

listview属性_属性提取器:获取ListView即时更新其元素的最佳方法

listview属性这篇文章是关于如何处理JavaFX ListViews和TableViews的&#xff0c;以及如何通过这些控件了解所包含元素的更改内容。 我想知道为什么在相关书籍中没有找到关于以下模式的任何信息&#xff0c;因为这是一个非常关键的机制。 那里的许多帖子建议通过调用以下命令来…

30分钟了解C 11新特性

什么是C 11C 11是曾经被叫做C 0x&#xff0c;是对目前C 语言的扩展和修正&#xff0c;C 11不仅包含核心语言的新机能&#xff0c;而且扩展了C 的标准程序库&#xff08;STL&#xff09;&#xff0c;并入了大部分的C Technical Report 1&#xff08;TR1&#xff09;程序库(数学的…