Cactoos是一个面向对象的Java原语库, 我们几周前才开始使用它。 目的是为JDK,Guava,Apache Commons等提供一种干净且更具声明性的替代方案。 我们不是使用静态过程,而是使用对象的使用方式,而是使用对象。 让我们看看输入/输出如何以纯面向对象的方式工作。
假设您要读取文件。 这是使用JDK7中的实用程序类 Files
的静态方法readAllBytes()
进行的操作:
byte[] content = Files.readAllBytes(new File("/tmp/photo.jpg").toPath()
);
这段代码非常重要-它会立即读取文件内容,并将其放入数组中。
这就是您使用Cactoos的方法 :
Bytes source = new InputAsBytes(new FileAsInput(new File("/tmp/photo.jpg"))
);
注意-还没有方法调用。 只有三个构造函数或三个类组成一个更大的对象。 对象source
的类型为Bytes
,代表文件的内容。 为了从中获取内容,我们将其方法asBytes()
:
bytes[] content = source.asBytes();
这是触摸文件系统的时刻。 如您所见,这种方法绝对是声明性的,并且由于它具有面向对象的所有优点。
这是另一个例子。 假设您要向文件中写入一些文本。 这是您在Cactoos中的操作方法。 首先,您需要Input
:
Input input = new BytesAsInput(new TextAsBytes(new StringAsText("Hello, world!"))
);
然后,您需要Output
:
Output output = new FileAsOutput(new File("/tmp/hello.txt")
);
现在,我们要将输入复制到输出。 纯 OOP中没有“复制”操作。 而且,根本不能进行任何操作。 只是对象。 我们有一个名为TeeInput
的类,它是一个Input
,它将您从其中读取的所有内容复制到Output
,类似于Apache Commons的 TeeInputStream
所做的TeeInputStream
,但被封装了。 因此,我们不进行复制,而是创建一个Input
,如果您触摸它,它将复制:
Input tee = new TeeInput(input, output);
现在,我们必须“触摸”它。 而且我们必须触摸它的每个字节,以确保它们都被复制了。 如果我们仅read()
第一个字节,则只有一个字节将被复制到文件中。 触摸所有对象的最佳方法是计算tee
对象的大小,逐字节地计算。 我们有一个对象,叫做LengthOfInput
。 它封装了一个Input
,其行为类似于其以字节为单位的长度:
Scalar<Long> length = new LengthOfInput(tee);
然后我们从中取出值,然后进行文件写入操作:
long len = length.asValue();
因此,将字符串写入文件的整个操作将如下所示:
new LengthOfInput(new TeeInput(new BytesAsInput(new TextAsBytes(new StringAsText("Hello, world!"))),new FileAsOutput(new File("/tmp/hello.txt")))
).asValue(); // happens here
这是它在JDK7中的替代程序:
Files.write(new File("/tmp/hello.txt").toPath(),"Hello, world!".getBytes()
);
“为什么面向对象即使更长,也更好?” 我听到你问。 因为它完美地解耦了概念,而过程化的概念却将它们保持在一起。
假设您正在设计一个类,该类应该加密一些文本并将其保存到文件中。 这是您将如何以程序方式设计它的方法(当然,这不是真正的加密):
class Encoder {private final File target;Encoder(final File file) {this.target = file;}void encode(String text) {Files.write(this.target,text.replaceAll("[a-z]", "*"));}
}
工作正常,但是当您决定扩展它以同时写入OutputStream
时,会发生什么? 您将如何修改此类? 那会多么丑陋? 那是因为设计不是面向对象的。
这就是您将使用Cactoos以面向对象的方式进行相同设计的方式:
class Encoder {private final Output target;Encoder(final File file) {this(new FileAsOutput(file));}Encoder(final Output output) {this.target = output;}void encode(String text) {new LengthOfInput(new TeeInput(new BytesAsInput(new TextAsBytes(new StringAsText(text.replaceAll("[a-z]", "*")))),this.target)).asValue();}
}
如果我们希望OutputStream
被接受,我们该怎么做? 我们只添加一个辅助构造函数:
class Encoder {Encoder(final OutputStream stream) {this(new OutputStreamAsOutput(stream));}
}
做完了 那是多么容易和优雅。
那是因为概念被完美地分离并且功能被封装了。 在该过程示例中,该对象的行为位于方法外部(在encode()
方法中encode()
。 该文件本身不知道如何写,有些外部过程Files.write()
知道。
相反,在面向对象的设计中, FileAsOutput
知道如何编写,而其他人FileAsOutput
知道。 文件写入功能被封装,这使得可以用任何可能的方式装饰对象,从而创建可重用和可替换的复合对象。
您现在看到OOP的美丽了吗?
翻译自: https://www.javacodegeeks.com/2017/06/object-oriented-declarative-inputoutput-cactoos.html