3. アルゴリズムを関数として分離
前述のプログラムをよく眺め、中のアルゴリズムについて考えてみましょう。
ProgramクラスのRun関数の中には、アルゴリズムが隠れています。例えば、次の2つです。
- 書籍のリストの中から、「書籍のタイトルまたは出版社の名前に "リ" を含むもの」を抽出する
- 書籍リストのそれぞれの書籍を表示する
この2つのアルゴリズムを関数として分離してみましょう。リファクタリングの始まりです。
Run 関数から2つの関数を分離したメイン プログラム
#include <iostream>
#include <list>
#include <vector>
#include <string>
using namespace std;
#include "Book.h"
#include "Publisher.h"
class Program
{
public:
void Run()
{
const vector<Publisher> publisherList = {
Publisher(L"技術評論社" ),
Publisher(L"翔泳社" ),
Publisher(L"オライリー・ジャパン"),
Publisher(L"SBクリエイティブ" )
};
const list<Book> bookList = {
Book(L"4774157155", L"C++ ポケットリファレンス" , 0),
Book(L"4798108936", L"C++ の絵本" , 1),
Book(L"4798119768", L"独習 C++ 第4版" , 1),
Book(L"4873110637", L"C++ プログラミング入門" , 2),
Book(L"4797376686", L"C++ テンプレートテクニック 第2版", 3)
};
const list<Book> filteredBookList = Filter(bookList, publisherList);
Show(filteredBookList, publisherList);
}
private:
// 書籍のリストの中から、
// 「書籍のタイトルまたは出版社の名前に "リ" を含むもの」を抽出する。
static list<Book> Filter(const list <Book >& bookList ,
const vector<Publisher>& publisherList)
{
const wstring searchWord = L"リ";
list<Book> filteredBookList;
for (list<Book>::const_iterator iterator = bookList.begin();
iterator != bookList.end(); iterator++) {
const wstring publisherName =
publisherList[iterator->GetPublisherIndex()].GetName();
if (iterator->GetTitle().find(searchWord) != wstring::npos ||
publisherName.find(searchWord) != wstring::npos)
filteredBookList.push_back(*iterator);
}
return filteredBookList;
}
// 書籍リストのそれぞれの書籍を表示する。
static void Show(const list <Book >& bookList ,
const vector<Publisher>& publisherList)
{
for (list<Book>::const_iterator iterator = bookList.begin();
iterator != bookList.end(); iterator++) {
wcout << L"コード: " << iterator->GetCode ()
<< L", タイトル: " << iterator->GetTitle()
<< L", 出版社: "
<< publisherList[iterator->GetPublisherIndex()].GetName()
<< endl;
}
}
};
int main()
{
wcout.imbue(locale("Japanese", locale::ctype));
Program().Run();
return 0;
}
実行結果は同じです。
コード: 4774157155, タイトル: C++ ポケットリファレンス, 出版社: 技術評論社 コード: 4873110637, タイトル: C++ プログラミング入門, 出版社: オライリー・ジャパン コード: 4797376686, タイトル: C++ テンプレートテクニック 第2版, 出版社: SBクリエイティブ
ここで、Run関数に注目してみましょう。
記述が明快になりました。
- 書籍のリストの中から、「書籍のタイトルまたは出版社の名前に "リ" を含むもの」を抽出し、
- それを表示する
という意図の部分だけがRun関数に記述されるようになり、forだとかiteratorのような意図以外の部分の記述がなくなって、分かりやすくなりました。
元の混沌としたRun関数から、書籍のリストに関して「抽出(Filter)」と「表示(Show)」という2つの部分を別関数として分けたためです。
これは重要なことです。
関数2つを分離したということは、「抽出(Filter)」や「表示(Show)」という名前でそれぞれの関心事を切り分けたことになります。そして、その「抽出(Filter)」と「表示(Show)」という新たな語彙でRun関数が記述できるようになった、ということです。
