2018-01-19

Cプログラマのための、
C++オブジェクト指向簡単裏口入門(7)

オブジェクトはどのように対象を写像するのか?(1)

名刺管理のアプリケーションの設計で考えていきましょう。
要点以外の詳細はできるだけ省きます。

オブジェクト指向の設計では、何がオブジェクトか、その全てを洗い出すところから始める、というのが常套手段です。でも、今回は設計手順を学ぶ記事ではないので、自由に考えていきます。

名刺管理ですから、当然名刺がオブジェクトになると考えてみます。では、名刺クラスを作りましょう。

コード1

// 名刺クラス定義
class NameCard
{
    string  m_name;         // 氏名
    string  m_mobTel;       // 携帯番号
    string  m_email;        // メアド
    string  m_title;        // 肩書き
    string  m_company;      // 所属会社
    string  m_department;   // 部署
    string  m_address;      // 会社住所
    string  m_tel;          // 電話番号
    string  m_date;         // 名刺交換日
    string  m_memo;         // メモ
};
とりあえず、名刺に記載されている情報と、名刺交換日とメモを加えてみました。
処理についてはまだ当分は後回しにします。

ところで、この名刺管理アプリにはどのような機能があるのでしょうか。(もちろん、本来であれば機能要件は先に決まっているはずです)

  • 検索機能(入力項目を条件にした絞り込み検索)
  • 宛名印刷(宛名ラベルやはがきの宛名印刷)

今回はこの2つの機能があると仮定します。

このうち宛名印刷は、個別の名刺宛に書類の送付等用以外に、年賀状等の一斉送付用も含めます。

さて、ここで機能上の問題が考えられます。

このクラスだと名刺1枚ずつをそのまま入力することになります。しかし、同じ会社に所属している人の名刺もたくさんあります。同じ会社なのに、毎回入力することになります。
とすると、年賀状を各所属会社に一通ずつ送付したいと思っても、所属が一括管理されていないので難しいというのはご理解いただけるでしょうか。
入力が正確で、一言一句、スペースの入力や全角半角の文字まで全て一致していて、なおかつ入力ミスが無ければ印刷するときに動的に名寄せする(重複を1つにまとめる)ことも可能ですが、人が入力する以上正確性は当てにできません。

この問題には印刷時だけでなく、所属会社の事務所が移転する等修正が必要になった場合等にもぶつかります。

そこで、所属する会社を分けて管理することが思いつきます。

コード2

// 名刺クラス
class NameCard
{
    string  m_name;         // 氏名
    string  m_mobTel;       // 携帯番号
    string  m_email;        // メアド
    string  m_title;        // 肩書き
    string  m_date;         // 名刺交換日
    string  m_memo;         // メモ
    Company * m_pCompany;   // 所属会社(NULLで所属無し)
};

// 会社クラス
class Company
{
    string  m_name;         // 会社名
    string  m_department;   // 部署
    string  m_address;      // 住所
    string  m_tel;          // 電話番号
};
会社クラスが新たにできました。そして、名刺クラスには会社クラスのオブジェクトを1つ所有できるようにしています。
これで概ね良さそうですか?

でも、もしこの名刺管理アプリを僕が使うのだとしたら困ることがあります。
同一人物が複数の会社やサークルなどに属していて、その人から所属の異なる名刺を複数枚もらうことがあるからです。
会社は名寄せした状態で管理できるようになりましたが、今度は人物です。

次のように解決しましょう。
名刺クラスと会社クラスの関係が、名刺クラス側から見た場合、今は1:0or1ですが、これを1:0~nとすることです。

ここで、ついでにもう1つ解決しておきたいことがあります。所属無しの名刺の場合(自由業など、個人名刺の場合)、連絡先住所などの入力がなくなってしまうので、名刺クラスの方にも住所などが必要になるということです。
この項目は個人の自宅住所になりますが、通常名刺には書かれていませんね。しかし、親交が深まる等で個人宅当てにお歳暮を贈る、ということは良くあることなので、会社などに所属している場合でも利用できます。。

では、コードを修正してみましょう。

コード3

// 名刺クラス
class NameCard
{
    string  m_name;                // 氏名
    string  m_mobTel;              // 携帯番号
    string  m_email;               // メアド
    string  m_address;             // ★1 住所(通常は自宅)
    string  m_title;               // 肩書き
    string  m_date;                // 名刺交換日
    string  m_memo;                // メモ
    vector<Company *> m_pCompanys; // ★2 所属会社 0~n
};

// 会社クラス
class Company
{
    string  m_name;         // 会社名
    string  m_department;   // 部署
    string  m_address;      // 住所
    string  m_tel;          // 電話番号
};
★1の部分、名刺クラスに住所項目を追加しました。
★2は、vector という動的配列で所属会社を複数登録できるようにしました。もちろん、要素数0も可能です。

ところで、ここまであえて書かなかったのですが、クラスをどのように実体化してプログラムの中に持つのでしょうか。疑問に思われた方もいるかも知れません。

個人も所属先の会社も名寄せした状態(重複させない)のですから、それぞれが配列のように実体を保持する状態だ、というところまではよろしいでしょうか。今はデータベースのことは考えず、メモリ上に全てのデータがあると考えてください。

コード4

vector<NameCard>    nameCards;
vector<Company>     companys;
単純に、それぞれは vector 配列でこんな感じで保持されていることになります。

NameCardクラスには Company クラスのポインタを要素とする配列の項目がありますね(コード3)。ここに、コード4の companys の要素のうち、所属するもののポインタを保持することになるわけです。

これで、とりあえず多対多が成立はするのですが、会社クラスには関連付けの項目が無いので、ある会社に着目して、所属社員全てを見たいときは、このままだと nameCards を全て洗い出さなければなりません。これでは非常に処理に時間を要してしまいますので、やはり、会社クラスの方にも関連付けのための項目を持たせておいた方が都合がいいですね。

コード5

// 名刺クラス
class NameCard
{
    string  m_name;                // 氏名
    string  m_mobTel;              // 携帯番号
    string  m_email;               // メアド
    string  m_address;             // 住所(通常は自宅)
    string  m_title;               // 肩書き
    string  m_date;                // 名刺交換日
    string  m_memo;                // メモ
    vector<Company *> m_pCompanys; // 所属会社 0~n
};

// 会社クラス
class Company
{
    string  m_name;                   // 会社名
    string  m_department;             // 部署
    string  m_address;                // 住所
    string  m_tel;                    // 電話番号
    vector<NameCard *> m_pNameCards;  // ★社員 0~n
};
会社クラスに社員項目を追加しました。

これで、NameCard 側からも Company 側からも所属が瞬時に判る訳です。もちろん、しっかりと整合性を持たせて、どちらかの片思いの状態にしてはいけません。

ただし、データの保持にデータベースエンジンを使用する場合、これらのクラスは一時的な使用に留まるため、関連付けの項目自体不要になる可能性もあります。関連付けはDB上で行われていて、関連のあるデータのみを読み出す場合が多いかもしれないからです。つまり、両方とも関連のあるデータしかメモリ上に存在しない状態であれば、関連付け情報自体必要が無いというわけです。
あるいは、もし関連情報が必要になった場合でも、その時点でデータベースに検索させる、ということも十分考えられます。

ところで、そろそろ説明に窮屈さを覚えてきましたので、1つ重要な用語を覚えてください。
今まで、漠然とオブジェクト、という言葉を使ってきましたが、「インスタンス」という言葉を登場させます。

インスタンスとは、クラスを実体化したオブジェクトのことです。クラスがハンコだとすると、それで押した印影がインスタンスです。クラスからインスタンスを作ることをインスタンス化すると言います。つまり、インスタンスは狭義の意味でのオブジェクトと言えます。
お堅いオブジェクト指向用語の1つですが、現場でもよく登場する言葉なので是非、覚えておいてください。

先ほどのデータベースの話に戻りますと、データベースから特定のデータを読み出してきたものをクラスのインスタンスに保持する、という言い方になります。


今回はここまでにします。
しかし、実はまだまだこれらのクラスには合理化できる余地がたくさん残されています。次の記事以後説明していきます。

0 件のコメント:

コメントを投稿