SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

連載記事

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

翔泳社では、「独習」「徹底入門」「スラスラわかる」「絵で見てわかる」「一年生」などの人気シリーズをはじめ、言語や開発手法、最新技術を解説した書籍を多数手がけています。プロジェクトマネジメントやチームビルティングといった管理職向けの書籍も豊富です。

ITエンジニアとしてのスキルアップ・リスキリングに、ぜひお役立てください。

書籍に関する記事を見る

'); googletag.cmd.push(function() { googletag.pubads().addEventListener('slotRenderEnded', function(e) { var ad_id = e.slot.getSlotElementId(); if (ad_id == 'div-gpt-ad-1659428980688-0') { var ad = $('#'+ad_id).find('iframe'); if ($(ad).width() == 728) { var ww = $(window).width(); ww = ww*0.90; var style = document.createElement("style"); document.head.appendChild( style ); var sheet = style.sheet; sheet.insertRule( "#div-gpt-ad-1659428980688-0 iframe {-moz-transform: scale("+ww/728+","+ww/728+");-moz-transform-origin: 0 0;-webkit-transform: scale("+ww/728+","+ww/728+");-webkit-transform-origin: 0 0;-o-transform: scale("+ww/728+","+ww/728+");-o-transform-origin: 0 0;-ms-transform: scale("+ww/728+","+ww/728+");-ms-transform-origin: 0 0;}", 0 ); sheet.insertRule( "#div-gpt-ad-1659428980688-0 div{ height:"+(90*ww/728)+"px;width:"+728+"px;}", 0 ); } else { if ($(window).width() < 340) { var ww = $(window).width(); ww = ww*0.875; var style = document.createElement("style"); document.head.appendChild( style ); var sheet = style.sheet; sheet.insertRule( "#div-gpt-ad-1659428980688-0 iframe {-moz-transform: scale("+ww/320+","+ww/320+");-moz-transform-origin: 0 0;-webkit-transform: scale("+ww/320+","+ww/320+");-webkit-transform-origin: 0 0;-o-transform: scale("+ww/320+","+ww/320+");-o-transform-origin: 0 0;-ms-transform: scale("+ww/320+","+ww/320+");-ms-transform-origin: 0 0;}", 0 ); sheet.insertRule( "#div-gpt-ad-1659428980688-0 div{ height:"+(180*ww/320)+"px;width:"+320+"px;}", 0 ); } } } }); }); } else { document.write('
'); document.write('
'); }
特集記事

よく使うC++のイディオム 「NVI」と「RAII」


  • X ポスト
  • このエントリーをはてなブックマークに追加

 アプリケーションを作るには、"何をつくるか(what)"、そしてそれを"どうつくるか(how)"を考え、しかるのちそれがコードとして書き起こされます。コーディングの段階では"いかにコンパクト/エレガント/堅牢につくるか"などを考慮した実装デザインがその後のデバッグや拡張/変更/保守に大きな影響を及ぼします。実装時のちょっとした工夫:NVIとRAIIについてざっくりと解説します。

  • X ポスト
  • このエントリーをはてなブックマークに追加

NVI:Non virtual Interface

 NVI(Non Virtual Interface)って、ご存じですか? 実装設計におけるオススメのポリシー/スタイルの一つなんですけど、そんな難しい話じゃありません。平たく言えば「パブリックメソッドをvirtualにしない」という実装上の制約です。それって何がオイシイんでしょう。

 日付を扱うアプリケーションのいち部品として、こんなのを考えました:

list01
class DateUtil {
public:
  const char* dayOfWeek(int nday) const;
  /* nday で与えられた 0,1...6 に対し、それぞれ
   * "日曜","月曜" ... "土曜" を返す */
  ...
};

const char* DateUtil::dayOnWeek(int nday) const {
  switch ( nday ) {
  case 0 : return "日曜";
  case 1 : return "月曜";
  ...
  case 6 : return "土曜";
  default: return nullptr;
}

 どこにでもありそうな、どってことのないコードです。日本語だけでなく英語にも対応しましょう。

list02
class DateUtil_ja {
public:
  const char* dayOfWeek(int nday) const;
  /* "日曜","月曜" ... "土曜" を返す */
  ...
};

class DateUtil_en {
public:
  const char* dayOfWeek(int nday) const;
  /* "Sun","Mon" ... "Sat" を返す */
  ...
};

 ワールドワイドな市場を狙ったアプリケーションの部品なら、これまたありがちなスペックですよね。

 ユーザのお好みやlocaleの設定次第で日/英をコロコロ切り替えたいならきっと:

list03
enum class language { ja, en };

class DateUtil { // 基底クラス
public:
  virtual const char* dayOfWeek(int nday) const=0;
  static DateUtil* create(language lang);
  ...
};

class DateUtil_ja : public DateUtil {
public:
  virtual const char* dayOfWeek(int nday) const;
  /* "日曜","月曜" ... "土曜" を返す */
  ...
};

class DateUtil_en : public DateUtil {
public:
  virtual const char* dayOfWeek(int nday) const;
  /* "Sun","Mon" ... "Sat" を返す */
  ...
};

DateUtil* DateUtil::create(language lang) {
  switch ( lang ) {
  case language::ja : return new Dateutil_ja();
  case language::en : return new Dateutil_ja();
  default:            return nulllptr;
}

 まぁ、まずまず妥当なデザインでしょうか。

 ここでNVIを適用します。NVIは「パブリックメソッドをvirtualにしない」がルールなので、dayOfWeek()をvirtualにできません。じゃぁどうするかっつーと、virtual仮想関数呼び出しのためにワンクッション置くんですわ:

list04
class DateUtil { // 基底クラス
public:
  const char* dayOfWeek(int nday) const {
    return do_dayOfWeek(nday); // dayOfWeekの"本体"を呼ぶ
  }
  static DateUtil* create(language lang);
protected:
  virtual const char* do_dayOfWeek(int nday) const =0;
...
};

class DateUtil_ja : public DateUtil {
protected:
  virtual const char* do_dayOfWeek(int nday) const;
  /* "日曜","月曜" ... "土曜" を返す */
  ...
};

class DateUtil_en : public DateUtil {
protected:
  virtual const char* do_dayOfWeek(int nday) const;
  /* "Sun","Mon" ... "Sat" を返す */
  ...
};

 動的に差し替わるvirtual関数をprotected部に置き、publicな関数からそいつを呼び出すことで実装しています。そのままprotectedなdo_dayOfWeek()に丸投げ/横流しですから、コードを無駄にややこしくしただけに見えます。

  • 「同じような処理があちこちにバラ撒かれる(Code Clone)ことを避けたい」
  • 「インターフェースの変更が実装に及ぼす影響を小さく抑えたい」

を実現するスタイルの一つがNVIです。

 public部にメソッドを置くということはすなわち、"使う人"にインターフェースを提供することを意味します。それと同時にそのメソッドを"作る人"に入出力の仕様を規定することになります。

 この両者が強く結びついていると一方の変更が他方に影響を及ぼします。そのこと自体はvirtualでないメソッドでも同じことなのですが、virtualメソッドのインターフェースの変更はそれを再定義しているすべての箇所に累が及ぶので、影響範囲がより広くかつ重大です。

 例えばこんな仕様変更:

 「dayOfWeek(int nday)だけどさ、ndayが0~6以外だったらdomain_error例外を投げてくんない?」

 dayOfWeek()がvirtualで、各導出クラスで再定義されていたらこの変更は導出クラスすべてにおよびます。この変更が身内の中だけならまだいいんですけど、ヨーロッパ支部のフランス/ドイツ対応チームがDateUtilからDateUtil_fr/DateUtil_deを導出してたら...

 NVIならば変更箇所は基底クラスの一箇所だけ。

list05
const char* DateUtil::dayOfWeek(int nday) const {
  if ( nday < 0 || nday > 6 ) {
    throw std::domain_error("invalid day-number");
  }
  return do_dayOfWeek(nday); // dayOfWeekの"本体"を呼ぶ
}

 「ついでにndayは1~7でお願い。処理中はCriticalSectionでガードしてね。んでもって戻り値はstd::stringにしてよ」なら、

list06
std::string DateUtil::dayOfWeek(int nday, CriticalSection* cs) const {
  if ( nday < 1 || nday > 7 ) {
    throw std::domain_error("invalid day-number");
  }
  EntrCriticalSection(cs);
  std::string result = do_dayOfWeek(nday-1); // dayOfWeekの"本体"を呼ぶ
  EntrCriticalSection(cs);
  return result;
}

 ちょっとした工夫なんだけど、クラス・ツリーの枝葉の多い構造であったならかなりの効果が期待できますよ。

会員登録無料すると、続きをお読みいただけます

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

次のページ
RAII:Resource Acquisition Is Initialization

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7294 2013/08/26 14:31
" ); }

おすすめ

アクセスランキング

  1. 1
    マイクロソフト、コンパイラとツールの移植でTypeScriptを10倍の処理速度へ NEW
  2. 2
    世界6000万ユーザーの「TimeTree」、サービスの未来を見据えて挑んだデータベース移行の舞台裏
  3. 3
    paiza、2027年卒ITエンジニア志望学生のインターンシップ人気企業ランキングを発表
  4. 4
    Oracle、プログラミング言語/開発プラットフォーム「Java 24」をリリース NEW
  5. 5
    データサイエンス基礎を高校数学から復習しよう! D3.jsでデータを表現する
  1. 6
    Laravelの新しいスタータキットでLivewireプロジェクトを作成──Livewireの基本的な使い方 NEW
  2. 7
    エンジニアが受けた「理不尽なクレーム」とは? バルテスが「理不尽なクレーム事例&対処法」を調査
  3. 8
    暗号化通信のためのオープンソースツール「OpenSSL 3.5.0」のalpha1バージョンがリリース NEW
  4. 9
    2025年4月に開催される注目のITエンジニア向けカンファレンス5選
  5. 10
    従業員1000名以上の大企業、45.7%が過去1年以内にセキュリティインシデントまたはサイバー攻撃の兆候を経験 NEW

アクセスランキング

  1. 1
    マイクロソフト、コンパイラとツールの移植でTypeScriptを10倍の処理速度へ NEW
  2. 2
    世界6000万ユーザーの「TimeTree」、サービスの未来を見据えて挑んだデータベース移行の舞台裏
  3. 3
    paiza、2027年卒ITエンジニア志望学生のインターンシップ人気企業ランキングを発表
  4. 4
    Oracle、プログラミング言語/開発プラットフォーム「Java 24」をリリース NEW
  5. 5
    データサイエンス基礎を高校数学から復習しよう! D3.jsでデータを表現する
  6. 6
    Laravelの新しいスタータキットでLivewireプロジェクトを作成──Livewireの基本的な使い方 NEW
  7. 7
    エンジニアが受けた「理不尽なクレーム」とは? バルテスが「理不尽なクレーム事例&対処法」を調査
  8. 8
    暗号化通信のためのオープンソースツール「OpenSSL 3.5.0」のalpha1バージョンがリリース NEW
  9. 9
    2025年4月に開催される注目のITエンジニア向けカンファレンス5選
  10. 10
    従業員1000名以上の大企業、45.7%が過去1年以内にセキュリティインシデントまたはサイバー攻撃の兆候を経験 NEW
  1. 1
    「オブザーバー・パターン」 ~マンガでプログラミング用語解説
  2. 2
    世界6000万ユーザーの「TimeTree」、サービスの未来を見据えて挑んだデータベース移行の舞台裏
  3. 3
    マイクロソフト、VSCodeの新機能「Copilot Next Edit Suggestions」のプレビュー版公開
  4. 4
    3/5まで書籍全文が無料公開 『Pythonで動かして学ぶ!Kaggleデータ分析入門』
  5. 5
    データサイエンス基礎を高校数学から復習しよう! D3.jsでデータを表現する
  6. 6
    macOS上のアプリケーションからChatGPTが呼び出せるように。各種IDEやターミナル、メモなどが対応
  7. 7
    ITエンジニア本大賞2025のプレゼン大会をレポート、最終決戦に臨んだ本に込められた想いとは
  8. 8
    GitHub、あらゆるエディタやIDEとGitHub Copilotとの統合を可能にする「Copilot Language Server SDK」を一般公開
  9. 9
    フロントエンドの定番ライブラリ「React 19」の新機能を紹介──React Server Componentsとその他の改善点
  10. 10
    Reactアプリ開発の環境構築ツール「Create React App」が非推奨に

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

メールバックナンバー

アクセスランキング

  1. 1
    マイクロソフト、コンパイラとツールの移植でTypeScriptを10倍の処理速度へ NEW
  2. 2
    世界6000万ユーザーの「TimeTree」、サービスの未来を見据えて挑んだデータベース移行の舞台裏
  3. 3
    paiza、2027年卒ITエンジニア志望学生のインターンシップ人気企業ランキングを発表
  4. 4
    Oracle、プログラミング言語/開発プラットフォーム「Java 24」をリリース NEW
  5. 5
    データサイエンス基礎を高校数学から復習しよう! D3.jsでデータを表現する
  1. 6
    Laravelの新しいスタータキットでLivewireプロジェクトを作成──Livewireの基本的な使い方 NEW
  2. 7
    エンジニアが受けた「理不尽なクレーム」とは? バルテスが「理不尽なクレーム事例&対処法」を調査
  3. 8
    暗号化通信のためのオープンソースツール「OpenSSL 3.5.0」のalpha1バージョンがリリース NEW
  4. 9
    2025年4月に開催される注目のITエンジニア向けカンファレンス5選
  5. 10
    従業員1000名以上の大企業、45.7%が過去1年以内にセキュリティインシデントまたはサイバー攻撃の兆候を経験 NEW

アクセスランキング

  1. 1
    マイクロソフト、コンパイラとツールの移植でTypeScriptを10倍の処理速度へ NEW
  2. 2
    世界6000万ユーザーの「TimeTree」、サービスの未来を見据えて挑んだデータベース移行の舞台裏
  3. 3
    paiza、2027年卒ITエンジニア志望学生のインターンシップ人気企業ランキングを発表
  4. 4
    Oracle、プログラミング言語/開発プラットフォーム「Java 24」をリリース NEW
  5. 5
    データサイエンス基礎を高校数学から復習しよう! D3.jsでデータを表現する
  6. 6
    Laravelの新しいスタータキットでLivewireプロジェクトを作成──Livewireの基本的な使い方 NEW
  7. 7
    エンジニアが受けた「理不尽なクレーム」とは? バルテスが「理不尽なクレーム事例&対処法」を調査
  8. 8
    暗号化通信のためのオープンソースツール「OpenSSL 3.5.0」のalpha1バージョンがリリース NEW
  9. 9
    2025年4月に開催される注目のITエンジニア向けカンファレンス5選
  10. 10
    従業員1000名以上の大企業、45.7%が過去1年以内にセキュリティインシデントまたはサイバー攻撃の兆候を経験 NEW
  1. 1
    「オブザーバー・パターン」 ~マンガでプログラミング用語解説
  2. 2
    世界6000万ユーザーの「TimeTree」、サービスの未来を見据えて挑んだデータベース移行の舞台裏
  3. 3
    マイクロソフト、VSCodeの新機能「Copilot Next Edit Suggestions」のプレビュー版公開
  4. 4
    3/5まで書籍全文が無料公開 『Pythonで動かして学ぶ!Kaggleデータ分析入門』
  5. 5
    データサイエンス基礎を高校数学から復習しよう! D3.jsでデータを表現する
  6. 6
    macOS上のアプリケーションからChatGPTが呼び出せるように。各種IDEやターミナル、メモなどが対応
  7. 7
    ITエンジニア本大賞2025のプレゼン大会をレポート、最終決戦に臨んだ本に込められた想いとは
  8. 8
    GitHub、あらゆるエディタやIDEとGitHub Copilotとの統合を可能にする「Copilot Language Server SDK」を一般公開
  9. 9
    フロントエンドの定番ライブラリ「React 19」の新機能を紹介──React Server Componentsとその他の改善点
  10. 10
    Reactアプリ開発の環境構築ツール「Create React App」が非推奨に