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