C++ の小さな技術を紹介するシリーズ【小技C++ 全９回】＃６＜オブジェクトの隠ぺい＞

#実装の隠ぺい
ライブラリを設計するとき、C++ では 「**pImplイディオム**」を使って実装の詳細を隠ぺいすることがよくある。本記事では、少し違った視点からの実装隠ぺいについて紹介していきたい。

#システムと内部モジュール
ここではまず、あるサービスを提供するクラスを「**System**」、内部クラスの１つを「**Module_A_System**」と定義する。**System** は常に **Module_A_System** インスタンスを保持しているとは限らないので、メンバ変数にはそのポインタとして持たせている。そして **Module_A_System**には、具体的なサービスであるところの「**Service**関数」が定義されている。

```cpp:module_a_system.h
class Module_A_System
{
public:
    void Service()
    {
        printf( "%s\n", secretStr.c_str() );
    }

private:
    const std::string m_secretStr = "call A_System::Service\n";
};
```
```cpp:system.h
// 前方宣言
class Module_A_System;

class System
{
private:
    Module_A_System* m_pModule_A_System;
};
```
ここで、ユーザにこのシステムを利用してもらうために、**System** クラスと **Module_A_System::Service** 関数を公開したい。しかし、 **Module_A_System** クラスが **private** な秘密のメンバ変数 **m_secretStr**を持っているので、このまま公開するわけにはいかない。

**Module_A_System** クラスを隠ぺいしつつ、**Module_A_System::Service** 関数を公開する手段が必要となる。

#ラッパークラス
それでは隠ぺいの手順について見ていこう。

まずは、**Module_A_System** クラスの代わりに、ユーザへ公開されるラッパークラスを定義する。

```cpp:module_a_wrapper.h
// 前方宣言
class Module_A_System;

class Module_A_Wrapper
{
public:
    void Service() const;

public:
    Module_A_Wrapper( Module_A_System* const );
    
private:
    Module_A_System* const m_pModule_A_System;
};
```
```cpp:module_a_wrapper.cpp
#include "module_a_wrapper.h"
#include "module_a_system.h"
//-------------------------------------
void Module_A_Wrapper::Service() const
{
    m_pModule_A_System->Service();
}
//-------------------------------------
Module_A_Wrapper::Module_A_Wrapper( Module_A_System* const pModule_A_System )
    : m_pModule_A_System( pModule_A_System )
{
}    
//-------------------------------------
```
コンストラクタで **Module_A_System** クラスのポインタをもらい、**Service** 関数をラップした同名の関数（同名でなくてもよい）を持たせている。

#ラッパーインスタンスの生成
ラッパーインスタンスは、**System**クラスの内部で生成する。そのために **System** クラスを以下のように拡張する。

```cpp:system.h
// 前方宣言
class Module_A_System;

class System
{
public:
    void Apply( const std::function<void( const Module_A_Wrapper& )>& );
    
private:
    Module_A_System* m_pModule_A_System;
};
```
```cpp:system.cpp
//-------------------------------------
#include "system.h"
#include "module_a_wrapper.h"
//-------------------------------------
void System::Apply( const std::function<void( const Module_A_Wrapper& )>& caller )
{
   if( m_pModule_A_System != nullptr ) {
        const Module_A_Wrapper module_a_wrapper( m_pModule_A_System );
        return caller( module_a_wrapper );
    }
    else {
      // サービスを提供できない
    }
}
//-------------------------------------
```
**System** クラスに新設された **Apply** 関数は、**Module_A_Wrapper**クラスを利用したいユーザ関数を受け入れる。**Apply** 関数の内部で一時的に生成されたラッパーインスタンスが、ユーザ関数の引数へ渡される。関数を抜けるとラッパーインスタンスは破棄される。ここで重要な点は **m_pModule_A_System** の **nullptr** チェックを行っているところだ。ラッパーインスタンスは、サービスを正しく提供できる場合にだけ生成される。これにより、ユーザが不適切なタイミングでサービスのアクセサにタッチしてしまうことをシステムが防いでくれている。

#Usage
```cpp
#include "system.h"
#include "module_a_wrapper.h"

{
    System system;
    
    const auto use_service = []( const Module_A_Wrapper& module_a ) {
        module_a.Service();
    };
    
    system.Apply( use_service );
}
```
#補足
実はもうひとつ、この手法には施しておくべきことがある。それは、ラッパークラスをコピー禁止にすることだ。ユーザ関数の引数として公開するラッパーインスタンスは先に述べたとおり、**Apply** 関数のリターンとともに消滅する。これをユーザ側でコピーしてしまうと以後危険な呼び出しとなってしまう。**noncopyable** なクラスにしておくのが親切だろう。

#使いやすさとのトレードオフ
今回紹介した手法は、ユーザにとっては一見遠まわしなサービスの使い方かもしれない。もっと簡単にアクセスしたいと思うユーザの気持ちもわかる。しかし、やり方を縛るということは、不自由な反面、安全性を確保してくれているとみることもできる。ユーザに責任を負わせても小回りが利くほうがよいか、それとも制限をつけて間違いを減らすか。これから作るシステムの性質と照らしあわせて考えていきたい。