ユース ケースに正確に適合しない可能性があるいくつかの例を含めましたが、保守と読み取りが少し簡単なコードを作成する方法をいくつか示します。
読みにくく、デバッグや保守が難しいコード。
- オブジェクトは入力 (コンストラクター引数) を検証する必要があります。
- 不良データを拒否します。後でデバッグするのが難しくなったときに、早く失敗してください。
- 回復できない限り、例外をキャッチしないでください。それを柔らかくする (ランタイムにラップして再スローする) か、throws 句に追加します。どうすればいいのかわからない場合は、何もしないでください。
- 例外を食べることはありません。投げ直すか、処理してください。
- あなたのコードは、それが必要ないことを述べ続けています。
- クラスは、gak の 2 つの配列よりも自己記述的です。
- public var は避けてください。それらが最終的なものでない限り。
- オブジェクトの状態を保護します。
- メソッドがどのように呼び出されるかを考え、副作用を避けてください。readData を 2 回呼び出すと、デバッグが困難な奇妙な副作用が発生する
- メモリは安価ですが無料ではありません。必要のない大きな配列をインスタンス化しないでください。
- 開いたら、閉じなければなりません。
Java 7 および 8 では、FileSystem から行を読み取ることができるため、最初からこのコードのほとんどを記述する必要はありません。
Path thePath = FileSystems.getDefault().getPath(location);
return Files.readAllLines(thePath, Charset.forName("UTF-8"));
多数の小さなファイルを行に読み込む必要があり、FileSystem を使用したくない場合、または Java 6 または Java 5 を使用している場合は、次のようにユーティリティ クラスを作成します。
public class IOUtils {
public final static String CHARSET = "UTF-8";
...
public static List<String> readLines(File file) {
try (FileReader reader = new FileReader(file)) {
return readLines(reader);
} catch (Exception ex) {
return Exceptions.handle(List.class, ex);
}
}
これは Reader を取る readLines を呼び出します:
public static List<String> readLines(Reader reader) {
try (BufferedReader bufferedReader = new BufferedReader(reader)) {
return readLines(bufferedReader);
} catch (Exception ex) {
return Exceptions.handle(List.class, ex);
}
}
BufferedReader を取る readLines を呼び出します。
public static List<String> readLines(BufferedReader reader) {
List<String> lines = new ArrayList<>(80);
try (BufferedReader bufferedReader = reader) {
String line = null;
while ( (line = bufferedReader.readLine()) != null) {
lines.add(line);
}
} catch (Exception ex) {
return Exceptions.handle(List.class, ex);
}
return lines;
}
Apache には、Apache コモンズ ( http://commons.apache.org/ )と呼ばれる一連のユーティリティがあります。これには lang が含まれ、IO ユーティリティ ( http://commons.apache.org/proper/commons-io/ ) が含まれます。Java 5 または Java 6 を使用している場合は、これらのいずれかがあればよいでしょう。
例に戻ると、任意の場所を行のリストに変えることができます。
public static List<String> readLines(String location) {
URI uri = URI.create(location);
try {
if ( uri.getScheme()==null ) {
Path thePath = FileSystems.getDefault().getPath(location);
return Files.readAllLines(thePath, Charset.forName("UTF-8"));
} else if ( uri.getScheme().equals("file") ) {
Path thePath = FileSystems.getDefault().getPath(uri.getPath());
return Files.readAllLines(thePath, Charset.forName("UTF-8"));
} else {
return readLines(location, uri);
}
} catch (Exception ex) {
return Exceptions.handle(List.class, ex);
}
}
FileSystem、Path、URI などはすべて JDK にあります。
例を続ける:
private static List<String> readLines(String location, URI uri) throws Exception {
try {
FileSystem fileSystem = FileSystems.getFileSystem(uri);
Path fsPath = fileSystem.getPath(location);
return Files.readAllLines(fsPath, Charset.forName("UTF-8"));
} catch (ProviderNotFoundException ex) {
return readLines(uri.toURL().openStream());
}
}
上記は FileSystem から uri を読み取ろうとし、読み込めない場合は URL ストリーム経由で検索します。URL、URI、ファイル、ファイルシステムなどはすべて JDK の一部です。
URL ストリームを Reader に変換し、次に使用する文字列に変換するには、次のようにします。
public static List<String> readLines(InputStream is) {
try (Reader reader = new InputStreamReader(is, CHARSET)) {
return readLines(reader);
} catch (Exception ex) {
return Exceptions.handle(List.class, ex);
}
}
:)
それでは、例に戻りましょう (ファイルを含むどこからでも行を読み取ることができるようになりました)。
public static final class Proxy {
private final String address;
private final int port;
private static final String DATA_FILE = "./files/proxy.txt";
private static final Pattern addressPattern = Pattern.compile("^(\\d{1,3}[.]{1}){3}[0-9]{1,3}$");
private Proxy(String address, int port) {
/* Validate address in not null.*/
Objects.requireNonNull(address, "address should not be null");
/* Validate port is in range. */
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port is not in range port=" + port);
}
/* Validate address is of the form 123.12.1.5 .*/
if (!addressPattern.matcher(address).matches()) {
throw new IllegalArgumentException("Invalid Inet address");
}
/* Now initialize our address and port. */
this.address = address;
this.port = port;
}
private static Proxy createProxy(String line) {
String[] lineSplit = line.split(":");
String address = lineSplit[0];
int port = parseInt(lineSplit[1]);
return new Proxy(address, port);
}
public final String getAddress() {
return address;
}
public final int getPort() {
return port;
}
public static List<Proxy> loadProxies() {
List <String> lines = IOUtils.readLines(DATA_FILE);
List<Proxy> proxyList = new ArrayList<>(lines.size());
for (String line : lines) {
proxyList.add(createProxy(line));
}
return proxyList;
}
}
不変の状態がないことに注意してください。これにより、バグが防止されます。また、コードのデバッグとサポートが容易になります。
ファイル システムから行を読み取る IOUtils.readLines に注目してください。
Proxy のインスタンスが不正な状態で初期化されないようにするための、コンストラクターでの余分な作業に注意してください。これらはすべて JDK オブジェクト、パターンなどにあります。
再利用可能な ProxyLoader が必要な場合は、次のようになります。
public static class ProxyLoader {
private static final String DATA_FILE = "./files/proxy.txt";
private List<Proxy> proxyList = Collections.EMPTY_LIST;
private final String dataFile;
public ProxyLoader() {
this.dataFile = DATA_FILE;
init();
}
public ProxyLoader(String dataFile) {
this.dataFile = DATA_FILE;
init();
}
private void init() {
List <String> lines = IO.readLines(dataFile);
proxyList = new ArrayList<>(lines.size());
for (String line : lines) {
proxyList.add(Proxy.createProxy(line));
}
}
public String getDataFile() {
return this.dataFile;
}
public static List<Proxy> loadProxies() {
return new ProxyLoader().getProxyList();
}
public List<Proxy> getProxyList() {
return proxyList;
}
...
}
public static class Proxy {
private final String address;
private final int port;
...
public Proxy(String address, int port) {
...
this.address = address;
this.port = port;
}
public static Proxy createProxy(String line) {
String[] lineSplit = line.split(":");
String address = lineSplit[0];
int port = parseInt(lineSplit[1]);
return new Proxy(address, port);
}
public String getAddress() {
return address;
}
public int getPort() {
return port;
}
}
コーディングは素晴らしいです。テストは神です!この例のいくつかのテストを次に示します。
public static class ProxyLoader {
private static final String DATA_FILE = "./files/proxy.txt";
private List<Proxy> proxyList = Collections.EMPTY_LIST;
private final String dataFile;
public ProxyLoader() {
this.dataFile = DATA_FILE;
init();
}
public ProxyLoader(String dataFile) {
this.dataFile = DATA_FILE;
init();
}
private void init() {
List <String> lines = IO.readLines(dataFile);
proxyList = new ArrayList<>(lines.size());
for (String line : lines) {
proxyList.add(Proxy.createProxy(line));
}
}
public String getDataFile() {
return this.dataFile;
}
public static List<Proxy> loadProxies() {
return new ProxyLoader().getProxyList();
}
public List<Proxy> getProxyList() {
return proxyList;
}
}
public static class Proxy {
private final String address;
private final int port;
public Proxy(String address, int port) {
this.address = address;
this.port = port;
}
public static Proxy createProxy(String line) {
String[] lineSplit = line.split(":");
String address = lineSplit[0];
int port = parseInt(lineSplit[1]);
return new Proxy(address, port);
}
public String getAddress() {
return address;
}
public int getPort() {
return port;
}
}
これは、すべて1つのクラスの代替手段です。(ProxyLoader にはあまり意味がありませんでした)。
public static final class Proxy2 {
private final String address;
private final int port;
private static final String DATA_FILE = "./files/proxy.txt";
private static final Pattern addressPattern = Pattern.compile("^(\\d{1,3}[.]{1}){3}[0-9]{1,3}$");
private Proxy2(String address, int port) {
/* Validate address in not null.*/
Objects.requireNonNull(address, "address should not be null");
/* Validate port is in range. */
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port is not in range port=" + port);
}
/* Validate address is of the form 123.12.1.5 .*/
if (!addressPattern.matcher(address).matches()) {
throw new IllegalArgumentException("Invalid Inet address");
}
/* Now initialize our address and port. */
this.address = address;
this.port = port;
}
private static Proxy2 createProxy(String line) {
String[] lineSplit = line.split(":");
String address = lineSplit[0];
int port = parseInt(lineSplit[1]);
return new Proxy2(address, port);
}
public final String getAddress() {
return address;
}
public final int getPort() {
return port;
}
public static List<Proxy2> loadProxies() {
List <String> lines = IO.readLines(DATA_FILE);
List<Proxy2> proxyList = new ArrayList<>(lines.size());
for (String line : lines) {
proxyList.add(createProxy(line));
}
return proxyList;
}
}
次に、テストを作成します (テストと TDD は、これらの問題を乗り越えるのに役立ちます)。
@Test public void proxyTest() {
List<Proxy> proxyList = ProxyLoader.loadProxies();
assertEquals(
5, len(proxyList)
);
assertEquals(
"127.0.0.1", idx(proxyList, 0).getAddress()
);
assertEquals(
8080, idx(proxyList, 0).getPort()
);
//192.55.55.57:9091
assertEquals(
"192.55.55.57", idx(proxyList, -1).getAddress()
);
assertEquals(
9091, idx(proxyList, -1).getPort()
);
}
idx などは、boon という独自のヘルパー ライブラリで定義されています。idx メソッドは、Python または Ruby のスライス表記のように機能します。
@Test public void proxyTest2() {
List<Proxy2> proxyList = Proxy2.loadProxies();
assertEquals(
5, len(proxyList)
);
assertEquals(
"127.0.0.1", idx(proxyList, 0).getAddress()
);
assertEquals(
8080, idx(proxyList, 0).getPort()
);
//192.55.55.57:9091
assertEquals(
"192.55.55.57", idx(proxyList, -1).getAddress()
);
assertEquals(
9091, idx(proxyList, -1).getPort()
);
}
私の入力ファイル
127.0.0.1:8080
192.55.55.55:9090
127.0.0.2:8080
192.55.55.56:9090
192.55.55.57:9091
そして、私のIOUtils(実際にはIOと呼ばれます)はどうですか:
これはIO(utils)を気にする人のためのテストです:
package org.boon.utils;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.*;
import java.util.regex.Pattern;
import static javax.lang.Integer.parseInt;
import static org.boon.utils.Lists.idx;
import static org.boon.utils.Lists.len;
import static org.boon.utils.Maps.copy;
import static org.boon.utils.Maps.map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
...
これにより、関連するインポートのアイデアが得られます。
public class IOTest {
....
以下は、ファイル システム上のファイルから行を読み取るテストです。
@Test
public void testReadLines() {
File testDir = new File("src/test/resources");
File testFile = new File(testDir, "testfile.txt");
List<String> lines = IO.readLines(testFile);
assertLines(lines);
}
ファイルが正しく読み取られたことをアサートするヘルパー メソッドを次に示します。
private void assertLines(List<String> lines) {
assertEquals(
4, len(lines)
);
assertEquals(
"line 1", idx(lines, 0)
);
assertEquals(
"grapes", idx(lines, 3)
);
}
これは、文字列パスからファイルを読み取ることを示すテストです。
@Test
public void testReadLinesFromPath() {
List<String> lines = IO.readLines("src/test/resources/testfile.txt");
assertLines(lines);
}
このテストは、URI からのファイルの読み取りを示しています。
@Test
public void testReadLinesURI() {
File testDir = new File("src/test/resources");
File testFile = new File(testDir, "testfile.txt");
URI uri = testFile.toURI();
//"file:///....src/test/resources/testfile.txt"
List<String> lines = IO.readLines(uri.toString());
assertLines(lines);
}
以下は、HTTP サーバーからファイルから行を読み取ることができることを示すテストです。
static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
File testDir = new File("src/test/resources");
File testFile = new File(testDir, "testfile.txt");
String body = IO.read(testFile);
t.sendResponseHeaders(200, body.length());
OutputStream os = t.getResponseBody();
os.write(body.getBytes(IO.CHARSET));
os.close();
}
}
HTTP サーバー テスト (HTTP サーバーをインスタンス化します) を次に示します。
@Test
public void testReadFromHttp() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(9666), 0);
server.createContext("/test", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
Thread.sleep(1000);
List<String> lines = IO.readLines("http://localhost:9666/test");
assertLines(lines);
}
プロキシ キャッシュ テストは次のとおりです。
public static class ProxyLoader {
private static final String DATA_FILE = "./files/proxy.txt";
private List<Proxy> proxyList = Collections.EMPTY_LIST;
private final String dataFile;
public ProxyLoader() {
this.dataFile = DATA_FILE;
init();
}
public ProxyLoader(String dataFile) {
this.dataFile = DATA_FILE;
init();
}
private void init() {
List <String> lines = IO.readLines(dataFile);
proxyList = new ArrayList<>(lines.size());
for (String line : lines) {
proxyList.add(Proxy.createProxy(line));
}
}
public String getDataFile() {
return this.dataFile;
}
public static List<Proxy> loadProxies() {
return new ProxyLoader().getProxyList();
}
public List<Proxy> getProxyList() {
return proxyList;
}
}
public static class Proxy {
private final String address;
private final int port;
public Proxy(String address, int port) {
this.address = address;
this.port = port;
}
public static Proxy createProxy(String line) {
String[] lineSplit = line.split(":");
String address = lineSplit[0];
int port = parseInt(lineSplit[1]);
return new Proxy(address, port);
}
public String getAddress() {
return address;
}
public int getPort() {
return port;
}
}
public static final class Proxy2 {
private final String address;
private final int port;
private static final String DATA_FILE = "./files/proxy.txt";
private static final Pattern addressPattern = Pattern.compile("^(\\d{1,3}[.]{1}){3}[0-9]{1,3}$");
private Proxy2(String address, int port) {
/* Validate address in not null.*/
Objects.requireNonNull(address, "address should not be null");
/* Validate port is in range. */
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port is not in range port=" + port);
}
/* Validate address is of the form 123.12.1.5 .*/
if (!addressPattern.matcher(address).matches()) {
throw new IllegalArgumentException("Invalid Inet address");
}
/* Now initialize our address and port. */
this.address = address;
this.port = port;
}
private static Proxy2 createProxy(String line) {
String[] lineSplit = line.split(":");
String address = lineSplit[0];
int port = parseInt(lineSplit[1]);
return new Proxy2(address, port);
}
public final String getAddress() {
return address;
}
public final int getPort() {
return port;
}
public static List<Proxy2> loadProxies() {
List <String> lines = IO.readLines(DATA_FILE);
List<Proxy2> proxyList = new ArrayList<>(lines.size());
for (String line : lines) {
proxyList.add(createProxy(line));
}
return proxyList;
}
}
@Test public void proxyTest() {
List<Proxy> proxyList = ProxyLoader.loadProxies();
assertEquals(
5, len(proxyList)
);
assertEquals(
"127.0.0.1", idx(proxyList, 0).getAddress()
);
assertEquals(
8080, idx(proxyList, 0).getPort()
);
//192.55.55.57:9091
assertEquals(
"192.55.55.57", idx(proxyList, -1).getAddress()
);
assertEquals(
9091, idx(proxyList, -1).getPort()
);
}
実際のプロキシ キャッシュ テストは次のとおりです。
@Test public void proxyTest2() {
List<Proxy2> proxyList = Proxy2.loadProxies();
assertEquals(
5, len(proxyList)
);
assertEquals(
"127.0.0.1", idx(proxyList, 0).getAddress()
);
assertEquals(
8080, idx(proxyList, 0).getPort()
);
//192.55.55.57:9091
assertEquals(
"192.55.55.57", idx(proxyList, -1).getAddress()
);
assertEquals(
9091, idx(proxyList, -1).getPort()
);
}
}
この例とこのユーティリティ クラスのすべてのソース コードは、次の場所で確認できます。
https://github.com/RichardHightower/boon
https://github.com/RichardHightower/boon/blob/master/src/main/java/org/boon/utils/IO.java
または、私に会いに来てください:
http://rick-hightower.blogspot.com/