Loading…
Transcript

の前に

グローバル変数を

disる。

グローバル変数が

なぜダメか。

どこで誰が書き換えたか

よく分からないから。

class Person

{

private:

  //Nameの書き換えができるのは

  //このクラスメソッドだけ

  std::string Name;

};

情報を制限する。

逆に言えば、

Name を適切に管理することは

Person クラスの責任である。

Name が変になったときは、

Personクラスが真っ先に疑われる。

そして、disられる。

Person テラカワイソス

いいクラス設計を

考える

の前に

ダメなクラスを

考える。

だめなクラスって

どんなクラス?

.cppが1万行ある

クラスを考える。

でかい

でかすぎる

どこで誰が何を

書き換えているのか

さっぱり

わかりません。

これでは、

メンバ変数が

グローバル変数と化す。

これの逆に

短いクラスを

考える。

ぢゃあ

どうするればいいの?

あれ?

これって関数と似てない?

反論:

これではクラス数が膨大になって、クラス数の爆発が起きませんか?

すっきり。

小さいクラスと

巨大なクラスの

見分け方は?

行数より

メンバ変数の数で

見てます。

もちろん、

例外もあります。

だけど、

人間は多くのものを

把握できないものです。

メンバ変数の爆発がダメなら、

メソッドの爆発はいいの?

ただし、

オブジェクトの状態を

変異させるメソッドには

注意が必要

setterを量産しては、

せっかく情報を隠した

意味がないです。

class Person

{

public:

Person(const std::string & inName)

: Name(inName)

{

}

std::string getName() const

{

return this->Name;

}

void setName

(const std::string & inName)

{

this->Name = inName;

}

private:

std::string Name;

};

情報を書き換えられる

メソッドは

できるだけ少なくします。

const!!

constメソッドは

自分はメンバ変数を

書き換えないと

宣言しているメソッドです。

class Person

{

public:

Person(const std::string & inName)

: Name(inName)

{

}

std::string getName() const

{

return this->Name;

}

private:

std::string Name;

};

状態が変異しないということは、

1000億回呼び出しても副作用がない。

それぞれを1000回呼び出すと?

なぜ

変更されるか、

変更されないかが

大事なの?

そもそもの議論を思い出す。

なぜグローバル変数が悪いのか。

どこで変更されるかわからない。

で、話を戻して、、

(*゚ ∀゚)=3ムッハー!

C++使っているなら

できるだけ

constメソッドを

量産しよう!!

継承について

継承は2つの意味があります。

C++だと

interface継承は純粋仮想クラス「だけ」で

構成したクラスという感じでしょうか。

class IPlayer

{

public:

virtual void Play() = 0;

};

interface継承は大歓迎

動画の再生ボタンと

CDプレイヤの再生ボタンは、

同じ再生だけどやることが違う

ボタンが押されたとき

class IPlayer

{

public:

virtual void Play() = 0;

};

class NetPlayer :

public IPlayer

{

virtual void Play()

{

ネットからダウンロードする

画像と音に変換する

画像を画面に出して

  音を鳴らす。

}

};

class CD :

public IPlayer

{

virtual void Play()

{

CDを回転して読み込む

データから音に変換する

音を鳴らす

}

};

interface継承は

なので

どんどんやりましょう。

実装の追記である

abstract継承は

注意が必要

悪いとはいいませんが、

やりすぎに注意して。

なぜか?

継承はクラスの

足し算だからです。

クラス同士を結合し、

より大きなクラスを生成します。

class Base

{

protected:

int Count;

public:

virtual int countUp()

{

this->Count += 1;

}

int getCount const()

{

return this->Count;

}

}:

継承

class SuperCount :

public Base

{

private:

int UpCount;

public:

virtual int countUp()

{

this->Count += this->UpCount;

}

int setUpCount(int inUpCount)

{

this->UpCount = inUpCount;

}

};

継承せずに

書くとこうなる。

class SuperCount{

private:

int Count;

int UpCount;

public:

virtual int countUp()

{

this->Count += this->UpCount;

}

int getCount const()

{

return this->Count;

}

int setUpCount(int inUpCount)

{

this->UpCount = inUpCount;

}

}:

巨大なクラスに

近づいてしまう。

よく考えて

継承するようにします

委譲

is-a has-a の関係と

実装の継承

基本的に継承が

許されるのは、

is-aの関係のときです

has-a の時に、

継承すると

不思議なことになります

has-a の時には、

委譲を行います。

委譲のメリット

小技

クラス内で確保したリソースは、

特別な理由がない限り、

デストラクタで解放する

メモリが必要な場合は

std::vector で確保

char * p = new char[100];

strcpy(p , "11111111");

delete [] p:

class Buffer

{

public:

Buffer()

{

this->Buffer = NULL;

}

virtual ~Buffer()

{

delete [] this->Buffer;

}

void Alloc(uint size)

{

this->Buffer = new char[size];

}

};

ちなみに先ほどのようなクラスは不要ですなw

雑用クラスの

Utilは何かと便利。。。

雑多なモジュールを入れるクラスを

一つ作っておくと便利です。

今までの内容と矛盾する?

クラスに

切り出すほどでもない、

同じアルゴリズムを

開発者ごと、

クラスごとに作るのは

効率が悪い。

1プロジェクトに

1つはUtilクラス欲しいところ。

最初に作っとかないと

それぞれの開発者が

独自に作るので効率が悪い。

非スレッドセーフ上等

特別な理由がない限り、

スレッドセーフを

個々のクラスが

気にすることではありません。

逆に、

個々のクラスが

スレッドセーフ性を

考えなければいけないような

状態は設計がおかしい

組織で

別部門の人に仕事を

依頼するときに

マネージャを通すのと同じ

ロックの粒度を考えると、

直接やり取りしたほうがいい。

別部門への依頼も直接やり取りの方が早い。

だけど、ハンドルしきれなくなるのも不幸の始まり。

結局は、どっちを取るかは、

バランスになるのかなと。

トレードオフですね。

書き込み可能な

シングルトンは要注意

singleton を利用すれば

クラスを超えて

データを

持ち運ぶことができます

でかいことは

悪いことだ。

これだけわかれば、

あとは大丈夫!!

たぶん。

えんいー\e

誰が書き換えたのか

よく分からないから

追跡ができない。

そこでデータの

カプセル化/情報の隠匿。

クラス設計のお話

~反論大歓迎www~

(クラスの持つ性質の2つのうちの1つ)

by rti

誰が管理しているのか、

誰が悪いのか、

責任の所在が

はっきりしない。

Name にアクセスし書き換えることが

できる機会を少なくすることで、

不用意なデータの書き換えで

プログラムがおかしくなることを防ぐ。

↑重要

Tシャツもあるよ

悲しいけど、でも、

これプログラムなんだよね。

長いクラス

巨大なクラス

↓ここから落としてみてね

http://rtilabs.net/files/2010_05_31/bigclass.cpp.txt

自由に使えるサンプルがなかったので、

自分で作ったプログラムを結合しまくって、

巨大なクラスを作りましたwww

クラス

やりたいこと

クラス

でかい

クラス

クラス

呼び出す

クラス

小さいクラスをたくさん作ります。

それを組み合わせて、

複雑なことを行います。

メンバ変数が

class

{

private:

int Age;

std::string Name;

std::string Tel;

std::string Address;

std::string Mail;

};

class Person

{

public:

Person(const std::string & inName)

: Name(inName)

{

}

std::string getName() const

{

return this->Name;

}

private:

//これを操作できるのは Personクラスだけ

std::string Name;

};

class

{

private:

int Age;

std::string Name;

std::string Tel;

std::string Address;

std::string Mail;

int Number;

int Salary;

std::string Section

std::string Title;

int Birthday;

};

つまり。

5個以上にぐらいで黄色信号

10個ぐらいになると赤信号

でかいクラスはダメなのです。

ダメな理由はグローバル変数が

ダメな理由と一緒です。

メンバー変数5個

rtiの独断と偏見にみちた基準

メンバー変数10個

どこでNameを書き換えているか

一発で分かります。

socket

db

クラス

namespace(C++) や

package(C#等) で

グルーピングします

クラス

html

クラス

私は手抜きなんで気に

しないんですけどw

クラス

小さいクラスがたくさん

グルーピング

邪悪

メソッドの爆発は

とりあえず大丈夫。

setter

これもrti基準

ネットからダウンロードする

画像と音に変換する

画像を画面に出して

  音を鳴らす。

CDを回転させてデータを読み込む

データから音に変換する

音を鳴らす

ここまでのまとめ

カプセル化してデータを隠そう。

カプセル化はクラスの2つの特性のうち1つ。

やることが違っても

constメソッドは、

C++で一番キュートで

セクシーな機能

ユーザーは同じ

キリッ

でかいクラスはよくない。

大きいことは悪いことだ。

class Person

{

public:

Person(const std::string & inName)

: Name(inName)

{

}

std::string getName() const

{

this->Name = "書き換え";

}

private:

std::string Name;

};

constかわいいよconst

インターフェースで

操作ができる。

状態を変異させるのはよくない。

constはかわいい。

かわいいは正義

これも独断と偏見ですw

再生ボタン

コンパイルエラーになる

誰が責任を取るのかを明確に

実装の継承

クラスの差分を追記する。

変異しない

C#でいう abstract継承

Person person;

int i ;

for (i = 0 ;i < 1000; i++)

{

person.getName();

person.countUp();

}

class Person

{

public:

Person(const std::string & inName)

: Name(inName) , Count(0)

{

}

std::string getName() const

{

return this->Name;

}

int countUp()

{

return ++this->Count;

}

private:

std::string Name;

int Count;

};

インターフェース継承

インターフェースを定義する。

変更されないというのは

その真逆ともいえる。

C#でいう interface継承

Name は変更はないが、

Count は 1000になる。

共通のインターフェースで

操作することができる。

これは何?

変異する

Play

これをポリモーフィズムといって、

日本語では多様性とかいいます。

大歓迎

(クラスの持つ性質の2つのうちのもう1つ)

is-a

同じカテゴリに含まれるような関係

「車」は「乗り物」のカテゴリ

has-a

持っている、所有しているのよう関係

is-aの関係

is-aの関係??

「車」は「車輪」をもっています

「車」 は 「乗り物」である。

「車」 は 「車輪」である???

個人的に、

継承ではないので

クラスの足し算が行われない。

アンチ実装の継承

//タイヤ

class Tire

{

};

なので、委譲大好きです。

//車はタイヤを4つもってる

class car

{

private:

Tire tire[4];

};

has-aの関係

クラスは巨大にならない

継承は数えるほどしかしませんが、

委譲は山のようにします。

委譲

has-aの関係!?

「車」 は 「車輪」をもっている

「車」 は 「乗り物」を持っている??

ここまでのまとめ

継承にはinterfaceの継承と、

実装の継承がある。

実装の継承はクラスの足し算になる。

継承は is-a の時にやって、

has-a のときは委譲しよう。

inerfaceの継承はクラスの特性の一つ

ポリモーフィズムを実現する。

std::vector<char> p(100);

strcpy( &p[0] , "11111111");

deleteし忘れがなくなるよ!!

std::vectorのみ &p[0] したときの連続性は保証されているよ。

アルゴリズムの重複

状態を持ちません。

(メンバ変数を持たない)

class Util

{

public:

//単語の数を数える

static void WordCount

{

}

};

class MyClassA

{

private:

int MyWordCount()

{

}

};

汎用的な処理がクラスに

ばら撒かれるのを防げます。

class MyClassB

{

private:

int OreOreWordCount()

{

}

};

似たような処理がたくさんできたら、

別クラスとして分離し独立させる

こともできます。

public static なのでどこからでも

インスタンス作成なしで呼べます。

Util::WordCount("/a/a/a","/");

マネージャを通す。

非スレッドセーフ

直接やり取りは効率がいいが

直接やり取り

設計が

おかしい

A部門

B部門

A部門

B部門

クラス

スレッド

クラス

スレッド

クラス

クラス

?

クラス

Xさん

クラス

Yさん

クラス

仕事の依頼

クラス

仕事の依頼

B部門マネージャ

?

B部門マネージャ

コイツが

スレッドセーフにする

責任を追う

マネージャがハンドルしきれない

誰も責任を取らない。。。。

マネージャ仕事しろ!!

書き込みsingletonには注意

int memoryLimit =

MySingleton::

getInstace()->

ReadMemoryLimit();

読み込みは仕方ない。

MySingleton::

getInstace()->

SetMemoryLimit(1024);

だって便利だし。。。

便利なんですが、

これってある意味

グローバル変数ですよね。

グローバル変数の悪夢再び

カプセル化

全体の

誰がデータを

書き換える権利/責任が

あるかを明確にします。

まとめ

ポリモーフィズ

別々のものを

同一インターフェースで、

利用できるようにします。