汚ねぇコードだ・・・
なんでもかんでもC#でやろうとしていたそんな時代。試行錯誤を重ねていたそんな時代。
エラーは頻発するしどこに原因があるかわかりもしない。
そんな時は大きな声で呼んでみよぅ、『せんぱぁ~い』。
先輩登場、スルスルとみていき原因解消ハイ解決。
そんな先輩が一言、『コード汚いな』。
こいつは手厳しいや!!!
その時に先輩に教えてもらったきれいなコードの書き方。今の先輩にその話をしても「そんなこと言った?」ときたもんだから、私のイマジナリー先輩に教えてもらったってことで、きれいなコードを書くための意識をちょっと書いていこうかと。まっ、「なんだそんなことか・・・」、と思われると思いますが、許してつかぁさい。
昔の私はなんでも同じ場所に
C#を始めたばかりの私はまずpublic static void Main()と宣言してとにかく書く!書く!書く!といった感じ。
何でもかんでもMainに突っ込みますし、同じ操作を行う場合でもその都度書いているといった所業。
再現?こんな感じですかね?
public static void Main() {
var input1 = @"D:tekitou1.txt";
var input2 = @"D:tekitou2.txt";
var list1 = new List<string>();
var list2 = new List<string>();
var output1 = @"D:out1.txt";
var output2 = @"D:out1.txt";
using (var sr = new StreamReader(input1)) {
while (sr.Peek() > -1) {
var line = sr.ReadLine();
list1.Add(line);
}
}
using (var sr = new StreamReader(input2)) {
while (sr.Peek() > -1) {
var line = sr.ReadLine();
list2.Add(line);
}
}
for (var i = 0; i < list1.Count; i++) {
var line = list1[i];
if (line.Contains("が")) {
line = "delete!";
list1[i] = line;
}
}
for (var i = 0; i < list1.Count; i++) {
var line = list1[i];
if (line.Contains("ぎ")) {
line = "delete!";
list1[i] = line;
}
}
using (var sw = new StreamWriter(output1)) {
foreach (var line in list1) {
sw.WriteLine(line);
}
}
using (var sw2 = new StreamWriter(output2)) {
foreach (var line in list2) {
sw2.WriteLine(line);
}
}
}
上のコードでは事前に用意した何かしらのファイルを2つ読み込んでそれぞれ別にListに加え、変更を加えた後に何かしらのファイルに出力するというもの。
かなり大げさにコードを書きましたが、うふふふ...、なんて読みにくいってわけですよ。
何故このコードが読みにくいか、答えは簡単、「同じ繰り返しが何度も存在している」から。
では、どのような意識をすれば読みやすくなるのか、イマジナリー先輩はこう言いました。
「コードは料理のレシピ作成」
コードは料理のレシピ作成
バニラクッキー、チョコレートクッキー、抹茶クッキー。
急に何を言い出すの?言いたいことは「製造工程は同じ」。
これらのクッキーは原材料が違うものの、製造工程は同じです。
つまりレシピが1つあればこれらのクッキーが作れるわけなんですね。
さて、ここで先ほどのコードを見てみましょう。基本的な操作は3つ
・ファイルを読み込んでListに格納
・List内の対応するものに変更を加える
・変更されたListを出力
これを2回繰り返しているだけ、つまりレシピが3つあれば簡単になりますね。
今回の場合はファイルを読み込んだタイミングで変更を加えてしまえばもっと簡素になりますが大げさに書いているのでよしなに。
というわけでレシピと称して分けると以下のように。
public static void Sub2() {
var input1 = @"D:tekitou1.txt";
var input2 = @"D:tekitou2.txt";
var list1 = new List<string>();
var list2 = new List<string>();
var output1 = @"D:out1.txt";
var output2 = @"D:out1.txt";
list1 = tameshi.Listin(input1);
list2 = tameshi.Listin(input2);
var check1 = "が";
var check2 = "ぎ";
list1 = tameshi.Listchange(list1, check1);
list2 = tameshi.Listchange(list2, check2);
tameshi.Listchange(list1, output1);
tameshi.Listchange(list2, output2);
using (var sw = new StreamWriter(output1)) {
foreach (var line in list1) {
sw.WriteLine(line);
}
}
using (var sw2 = new StreamWriter(output2)) {
foreach (var line in list2) {
sw2.WriteLine(line);
}
}
}
public static List<string> Listin(string input1) {
var list = new List<string>();
using (var sr = new StreamReader(input1)) {
while (sr.Peek() > -1) {
var line = sr.ReadLine();
list.Add(line);
}
}
return list;
}
public static List<string> Listchange(List<string> list, string check) {
for (var i = 0; i < list.Count; i++) {
var line = list[i];
if (line.Contains("が")) {
line = "delete!";
list[i] = line;
}
}
return list;
}
public static void Outputwriter(List<string> list, string output) {
using (var sw = new StreamWriter(output)) {
foreach (var line in list) {
sw.WriteLine(line);
}
}
}
上のコードを大雑把にイメージ化するとこんな感じ。
全体工程を進めるレシピが存在し、さらに引用する形で他の小さいレシピが存在している。
小さいレシピの完成品を利用して全体工程がさらに進められる。
レシピを分けると何が良いか。メリットしかないやるしかない。
一番は何といっても「繰り返しが抑えられる」こと。それだけ?ノンノン、これが大事。
繰り返しをいちいち書いていたらコードの量はどんどん増えていく。そして意図した挙動でない場合、コード全体を見返し、どこが悪さをしているかを特定しなくてはならない。そして特定できたとして、繰り返し部分であれば全部直す必要がある。
レシピを分けたら?操作が細分化されているために、どのレシピが悪さをしているかわかりやすい上、レシピを修正すればそのレシピを使った全ての出力が修正される。
まとめ
いかがでしたでしょうか。
「コードは料理のレシピ作成」
これ結構私の中でしっくり来てるんですよねぇ。
やっぱり、レシピって誰が使っても作れるのもメリットじゃないっすか?
この意識のおかげでコードは書きやすくなったし、見栄えも良し?
この場を借りて、ありがとうございます、(イマジナリー)先輩。