• Save
Cpp v3
Upcoming SlideShare
Loading in...5
×
 

Cpp v3

on

  • 750 views

 

Statistics

Views

Total Views
750
Views on SlideShare
750
Embed Views
0

Actions

Likes
0
Downloads
0
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Cpp v3 Cpp v3 Document Transcript

  • 1
  • 2
  • 3
  • ◆クラスとインスタンスオブジェクト指向はオブジェクトを基本に考えていきますが、このオブジェクトの設計書がクラスです。クラスという設計書から実際のオブジェクトを作り出すわけですが、この作り出されたオブジェクトが「実体化されたモノ」であることを特に明確に表す言葉としてインスタンスという言葉も使われます。ただし、「オブジェクト」と「インスタンス」は、それほど大きな違いがあるわけではなく、ほとんど同じ意味で使われます。◆継承と汎化人間は物事を整理するときに、同じ特質を持つものを同じ種類として分類します。さらにその種類の上位に、より大きな単位で同じ特質を持つグループを作ったりします。たとえば人間という動物を「霊長目ヒト科」として分類して理解するのがこれにあたります。こうしておけば、情報を整理し、体系的に理解することができます。たとえば霊長目の特質を知れば、「人間」の特質を知ることができると同時に、霊長目に属する他の動物(ニホンザルなど)についての特質も知ることができます。これをソフトウェアの設計や実装で実現するのが継承と汎化という概念です。したがって、継承や汎化を適切に使うことで、多くの情報を整理してわかりやすくすることができます。◆カプセル化オブジェクト指向では、個々のオブジェクトを独立した存在として考えます。オブジェクトが独立して存在するためには、オブジェクトの内と外を明確に分け、中身が外からむやみに触れられないようにする必要があります。たとえば、携帯電話が1つのオブジェクトである場合、中にある基板を勝手にいじると、携帯電話というオブジェクトの動作が保証されなくなります。そこで、オブジェクトの中身を外から簡単には操作できないようにする必要かあります。このようにオブジェクトの中身を隠すことをカプセル化と言います。◆インターフェースカプセル化を行った場合でも、そのオブジェクトには外からアクセスできるようにする必要があります。このオブジェクトへのアクセス方法を定義しているのがインターフェースです。インターフェースでは、どのような名前で呼び川し、どのような情報を入力すれば、どのような情報が返ってくるかを定義します。 4
  • インターフェースが明確になっていれば、オブジェクトの中で何が行われているかをまったく知らなくても、そのオブジェクトを外側から利用することができます。◆具象クラスと抽象クラスクラス内には、そのオブジェクトが外部からインターフェースを通じて呼び出されたときに行う処理を記述しておく必要があります。具体的な処理を含み、実際にインスタンス化できるクラスのことを具象クラスと言います。それに対して、インターフェースの定義だけで具体的な処理をまったく(あるいは一部しか)含まず、直接的にはインスタンス化できないクラスのことを抽象クラスと言います。◆多態性/多様性これらはオブジェクト指向の中でも特にわかりにくい概念です。しかし、これも継承や汎化と同様に、物事を表現するための情報を整理し、効率的にやり取りすることを可能にする考え方です。あるオブジェクトが他のオブジェクトを呼び出す場合のことを考えてみましょう。呼び出し側オブジェクトには解決したい問題があり、それを処理してもらうために他のオブジェクトを呼び出します。このとき、呼び出し側オブジェクトの目的は「問題を解決すること」であって、「オブジェクトを呼び出すこと」ではありません。つまり、問題を解決できさえすれば、相手側がどのオブジェクトであろうとも関係ないのです。これを利用して、呼び出し側オブジェクトにはある問題を解決するために使うインターフェースだけを知らせておき、実際にその処理を行うときには、相手側がどのオブジェクトなのかに関係なく、決まったインターフェースを呼び出すようにすることができます。こうすると、呼び出し側が知っていなければいけない情報を減らしつつ、複雑な処理を実現できるようになります。この考え方と仕組みが多態性・多様性です。◆集約と委譲オブジェクト指向では、オブジェクトは互いに独立して存在すると考えますが、オブジェクトの中でも関係性の深いオブジェクト群があったり、あるオブジェクトが他のオブジェクトに依存していたりすることもあります。こうしたオブジェクトの依存関係を示すのに使われるのが集約です。この概念は、再利用を考えたときに重要になってきます。非オブジェクト指向のプログラミングでは、ライブラリという形で処理を何度も再利用できるようにしていました。オブジェクト指向では、処理とデータを含む「オブジェクト」を単位として再利用を行う場合があります。この再利用では、オブジェクトどうしは集約の関係になり、実行したい機能を持つオブジェクトに処理を任せることになります(これを委譲と言います)。この方法を適切に使うとオブジェクト指向の効果を最大限に発揮できます。◆メッセージ送信オブジェクトは互いに独立していて、複数のオブジェクトが協調して動作します。このとき、あるオブジェクトが他のオブジェクトにメッセージを送信することで要求を出します。このメッセージのやり取りによって一連の処理や動作を遂行します。◆UMLオブジェクト指向で分析・設計を行い、設計書を記述する場合には、オブジェクト指向の考え方を視覚的に示すための表現手段が必要です。また、その表現手段は、自分だけでなく他の人も読むことができるように統一化されていなければなりません。こうした要求を満たすオブジェクト指向設計のための表記法がUML(Unified Modeling Language:統一モデリング言語)です。オブジェクト指向の設計を表現する際にはUMLが使われるのが一般的で、UMLはオブジェクト指向開発者の必須知識と言えます。 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • (1) 基本フローに記述するのは、正常ケースの中の一つの流れのみ基本フローには、正常終了する一つの流れのみを記述します。その流れ通りにはならない場合は、すべて代替フローに記述します。(2) 代替フローには、実行される条件を明示する代替フローの一行目「2a 条件に一致する書籍が一つもない場合」という記述を見てください。最初の数字「2」は、基本フローの2行目で起こり得るフローということを、「a」は識別子を意味します。“条件に一致する書籍が一つもない場合”という部分は、代替フローが実行される条件です。(3)概念モデルに対して何をするのかという観点で記述する基本フローのシステムが実施する内容を見てください。2行目と4行目がそれです。それぞれに“書籍”という記述があります。これは、概念モデル上で出てきた“書籍”と同一のものです。概念モデルはシステムの静的な構造を、ユースケースはシステムの動的な構造を表しますから、ユースケース上に書くシステムが実施すべき内容というのは、概念モデル上の概念を操作することに相当するわけです。(4) ユーザー・インタフェースの操作は記述しないユースケース記述の中には、前述のようにユーザー・インタフェースに関する記述は一切入れません。ユーザー・インタフェースの設計は、別途、画面仕様書なり画面遷移図なりを作成しそちらで行います。(5) 各フローの中にすべてを記述しようとしないフローの中にすべてを記述しようとしても無理があるので、備考をうまく利用してなるべくわかりやすく書きます。上記の例では検索条件の詳細を備考に記述したり、ログのフォーマットは別ドキュメントを参照するように記述しています。すべてをユースケース記述の中に記述する必要はないのです。 25
  • 26
  • 27
  • 28
  • 29
  • 1.概念候補の導出システム化の対象となる領域にどのような情報があるのかを抽出する作業とにかく思いついたものをどんどん出すのがコツ例えば、目に見えるもの(顧客、店、商品)、目には見えないが業務上管理しているもの(口座、在庫)、業務上の重要なイベント(注文、売上、貸し出し)といった指針に基づいて抽出するとよい2.概念候補のグループ化導出した概念候補の中で結びつきの強いものを集めてグループ化する作業ここで実施するグループ化は、概念モデリングを円滑に進めることを目的としたものなので、グループ化の基準をあまり気にする必要はないどのグループにも入らない概念候補がある場合は、“その他”のようなグループを作る3.概念の特定ここから先の作業は、各グループ別に行う洗い出した各概念に対して以下のような観点でチェックし、概念として適当ではないものを除外していく●同じ言葉でも、二つ以上の異なる意味があれば分割を検討する●違う言葉でも、同じ意味のものがあればどちらかにまとめることを検討する●何らかの概念の属性となるべきものをふるい落とす●概念として適切な名前に変更する(例:「会計伝票」→「会計取引」。伝票そのものが重要なのではなく、伝票の中の情報が重要だから)4.主属性の定義各概念が何を表すのかをわかりやすくするために、主属性を定義するそのときの指針として、●各概念ごとに定義する属性は三~五つぐらいまで●属性をすべて洗い出すことが目的ではない●属性ではなく別概念となる場合もある●(現段階では)データモデリングはしていないので、すべての概念に主キーを定義する必要はない 30
  • 5.関連の定義これまでの作業が完了した各概念間の“関連(Association)”を定義する関係(Relationship)を持つ概念の間に、関連を示す線を引き、多重度を定義する 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • アーキテクチャパターンアーキテクチャパターンは、「システム全体のアーキテクチャを決定する」段階で適用されます。アーキテクチャ(architecture)には「構造」という意味があり、ソフトウェア開発で「アーキテクチャ」と言えば、抽象度の高い構造的なまとまりのことを指します。代表的な例としてはMVC(Model-View-Controler)アーキテクチャが挙げられます。MVCアーキテクチャは、データ管理を担当するコンポーネント(Model)、Modelへの変更を担当するコンポーネント(Controller)、そしてユーザー向けの情報表現を担当するコンポーネント(View)から成ります。各コンポーネントはそれぞれの仕事を専門的に担当します。MVCアーキテクチャにはいくつかのバリエーションが存在し、VからMの矢印がない場合もあります。MVCは、システム内の責任分担を明確にして、システムの機能拡張を容易にするアーキテクチャパターンです。アナリシスバターンアナリシスパターンは「分析」の段階で適用されます。この段階では「ビジネスを人がどのように捉えているのか」を分析します。したがって、コンピュータのシステムに依存しないモデルを作成します。アナリシスパターンの一例として「パーティパターン」があります。ここで言う「パーティ」とは社交的な集まりのことではなく、「一行・グループ」の意味です。ビジネスの世界では、人と組織には似通った性質があります。たとえば人も組織も電話番号を持っていて、銀行の口座を持ったり、税金を申告したりすることもできます。そのため人と組織の類似部分を「パーティ」としてひとまとめに考えることができます。アナリシスパターンの1つとして、このような分析法をパーティパターンと呼んでいます。デザインバターンデザインパターンは「設計」の段階で適用されます。デザインパターンはアーキテクチャパターンやアナリシスパターンよりも粒度が小さく、イディオムよりも粒度が大きいということを覚えておいてくださ 58
  • い。イディオムイディオムは「コーディング」の段階で通用され、「プログラミングパターン」とも呼ばれます。イディオムとは、特定のプログラミング言語に特化したパターンであり、具体的なソースコードとして提示されることがよくあります。 58
  • 59
  • 楕円はパターンを、楕円の大きさはパターンの粒度を表しています。上図を見てもわかるとおり、パターンの粒度はアーキテクチャパターンからイディオムに向けて小さくなっていきます。楕円を結んでいる矢印とその方向は、パターンの使用関係を表しています。矢印の方向は上から下に向かっており、これはアーキテクチャパターンがアナリシスパターンを使用し、アナリシスパターンがデザインパターンを使用するということを表しています。つまり上から下に向けて「使用する・される」関係にあることがわかります。一般的には、粒度の粗いパターンが粒度の細かいパターンを内包し、使用することが可能です。 60
  • ◆生成・アブストラクトファクトリ(AbstractFactory)関連するオブジェクトの生成方法をグループ化し、具体的なオブジェクトを特定せずに生成するためのインターフェースを提供する。・ビルダー(Builder)オブジェクトの生成処理を担当するクラスを作成し、さまざまな生成処理を行えるようにする。・ファクトリメソッド(FactoryMethod)インターフェースを利用することで、直接具象クラスを明示してオブジェクト生成を行う必要をなくす。・プロトタイプ(Prototype)サンプルをコピーして生成したオブジェクトをクライアントに提供する。・シングルトン(Singleton)システム内に存在するオブジェクトが1つであることを保証する。◆構造・アダプター(Adapter)インターフェースの変換を行うことによって、互換性のないクラスを利用できるようにする。・ブリッジ(Bridge)抽象的なインターフェースを2つに分割し、それぞれを自由に変更できるようにする。・コンポジット(Composite)集団を表現するオブジェクトと要素を表現するオブジエクトを同じように扱えるようにする。・デコレータ(Decorator)オブジェクトに動的に機能を追加するための構造を提供する。サブクラス化による機能追加より柔軟な方法である。・ファサード(Facade)サブシステムへの統合されたインターフェースを提供することによって、サブシステムの利用方法を簡素化する。・フライウェイト(Flyweight) 61
  • 細粒度のオブジェクトを共有することで、オブジェクトの格納コストを減少させる。共有されるオブジェクトは使用状況に依存してはならない。・プロキシー(Proxy)WebサーバーやDBサーバーなど|こアクセスするプログラムを開発するときの便宜を図る。◆振る舞い・チェーンオブレスポンシビリティ(Chain of Responsibility)クライアントからの要求を処理できるオブジェクトをチェーンでつなぎ、そのチェーン中の任意のオブジェクトが要求を処理するようにする。・コマンド(Command)クライアントからの要求をオブジェクトを用いてカプセル化する。何かを行うタイミングはわかるが、具体的な処理内容がわからないときに使用する。・インタープリタ(Interpreter)言語の文法をオブジェクトで表現し、そのオブジェクトを用いてその言語の文を解釈する。・イテレータ(Iterator)集約オブジェクト内の個々のオブジェクトにアクセスするための統一化された手段を提供する。・メディエータ(Mediator)オブジェクト間の複雑な協調関係を集中管理するオブジェクトを定義する。これによりオブジェクトどうしの結合度を弱め、柔軟性を保つことができる。・メメント(Memento)カプセル化を侵害しない方法でオブジェクトの内部状態を保存し、後でその状態に戻れるようにする。・オブザーバー(Observer)あるオブジェクトが状態を変えたときに、そのオブジェクトに依存するオブジェクトに自動的に状態変更が通知され、その依存オブジェクトが更新されるまでの仕組みを提供する。・ステート(State)各状態における振る舞いを記述した状態オブジェクトを導入する。状態によって変化する振る舞いをクラス内に記述する必要がなくなり、状態オブジェクトに処理を任せることができるようになる。・ストラテジー(Strategy)アルゴリズムの集合を定義して実装を力プセル化しておき、クライアント側に影響を与えずにアルゴリズムを変更・交換できるようにする。・テンプレートメソッド(TemplateMethod)スーパークラスのメソッドで手順を決めておき、順番に呼び出されているメソッドについては、実装をサブクラスに任せることにする。・ビジター(Visitor)クラスの要素を変更せすに、新しい操作を定義できるようにする。 61
  • 62
  • 通常、オブジェクトを生成するときはnewを使ってコンストラクタを呼び出します。コンストラクタを呼び出した回数だけ、初期化されたオブジェクトが生成されます。オブジェクトはシステムの中のあらゆる箇所からあらゆるタイミングで利用される可能性がありますが、そのたびに初期化されてしまっては都合が悪い場合もあります。このような場合には、システムの起動中はオブジェクトを1つだけに制限したいという要件が出てきます。しかし、利用する側で生成するオブジェクトの数を管理するのは非常に困難です。そうした場合に役に立つのがシングルトンパターンです。シングルトンを適用すると、いつでもどこでも必ず同じオブジェクトを利用することができます。例としてプリンタのスプーラーを考えてみましょう。次の前提条件があるとします。・プリンタは1台でコンピュータとは1つのポートで接続されている。・同時には2つ以上のドキュメントの印刷はできない(当然ですが)。・複数のオブジェクトの各々から印刷要求が発行される。 63
  • プリンタは1つしかないことです。したがって、プリンタのスプールを管理するPrintSpoolオブジェクトは、常にシステム内に1つだけ存在しなければなりません。 64
  • main関数では、システム起動時にPrintSpoolオブジェクトを1つ生成します。この唯一のPrintSpoolオブジェクトを複数のオブジェクトが共有して利用できるようにするには、Processオブジェクトの作成時にPrintSpoolオブジェクトをパラメータとして渡します。 65
  • Processクラスでは、印字要求するprintOut()メソッドを定義します。このメソッドは印字したい文字列を受け取り、スプールに出力します。実際のプリンタへの印字要求はスプールが行うことになります。このままでもスプール処理は問題なく動作します。しかし、プリンタは1台であるという前提条件のため、スプールは通常1つであり、PrintSpoolオブジェクトも1つでなければなりません。ところが現状の実装では、コードのどこかで次の記述を行えば、簡単に別のPrintSpoolオブジェクトを生成できてしまいます。PrintSpool* obj = new PrintSpool(); 66
  • シングルトンパターンでは、「オブジェクトを1つだけ生成する」というロジックをそのクラス自身に持たせます。今回の例では、PrintSpoolクラス内にオブジェクト生成の制限ロジックを含めることになります。まず、外部のクラスがPrintSpoolクラスのオブジェクトをnewで生成できないようにします。そのためには、次のようにしてコンストラクタをprivateにします。しかし、このままでは代入によるコピーも可能となるため、コピーコンストラクタと代入演算子もあわせてprivateにする必要があります。 67
  • さらに、呼び出し側のクラス(関数)に唯一のPrintSpoolオブジェクトを提供するためのgetInstance ( )クラスメソッドを準備します。このクラスメソッドは、instanceというstatic変数に保持しているPrintSpoolオブジェクトのポインタを返します。instanceは、初めてgetlnstance ( )が呼び出されたときに、PrintSpoolのインスタンスを持つため、不変で唯一のオブジェクトが保証されます。クラス内にこのような仕組みを用意しておくと、呼び出し側クラスがオブジェクトの有無を管理する必要がなくなり、PrintSpool::getlnstance()という呼び出し方で、いつでもどこからでも唯一のオブジェクトを取得できます。ここまでが、シングルトンの基本となります。ポイントは次の2点です。・コンストラクタをプライベートにし、外部からアクセスできないようにする・getInstance( )クラスメソッドを用意し、プライベートクラス変数に保持している唯一のオブジェクトを返すようにする 68
  • 69
  • パターン適用後のPrintSpoolクラスには、シングルトンのロジックを実現するgetlnstance()メソッドが用意されています。Processクラスは、コンストラクタの引数としてPrintSpoolオブジェクトを受け取るのではなく、printOut()メソッドの中でPrintSpoolクラスのgetlnstance ( )メソッドを呼び出すことで、唯一のPrintSpoolオブジェクトを取得します。その結果、main関数内で, PrintSpoolオブジェクトを生成してProcessに渡す必要がなくなっています。 70
  • マルチスレッドのアプリケーションで本パターンを使用する場合は注意が必要。万が一、同時に複数のスレッドからgetInstance() が呼ばれた場合に予期しない動作となる可能性があります。その場合は排他制御を行ってgetInstance()に対する同時アクセスを回避する必要があります。 71
  • ファクトリメソッドパターンでは、インターフェースを介することで、オブジェクトの生成を柔軟に行えるようにします。通常、オブジェクトを生成するときには、その具象クラスのコンストラクタを呼び出します。OutputStream pStrearn1 = new BufferedOutputStream();OutputStream pStream2 = new BufferedOutputStream();この方法では具象クラスの名前を明示的に記述するので、同じクラスのオブジェクトを複数生成したいときには、そのクラスの名前がコードの複数箇所に出てくることになります。このような単純な方法には、コードの拡張や保守という点で問題があります。具象クラスを明示してオブジェクトを生成していると、使用するクラスを切り普えたいときや、新たにクラスを追加するときに、具象クラス名が出てくる箇所をすべて修正しなければならなくなり、柔軟性に欠けてしまいます。ファクトリメソッドパターンでは、インターフェースを介してオブジェクトの生成を行うことで、生成される側と生成する側の結びつき(結合度)を弱め、クラスの切り替えや新規クラスの追加を柔軟に行えるようにします。ソフトウェア開発の現場では、初めはダミークラスなどを使って開発を進め、後で実際のクラスに切り替えたいという要求が発生する場合があります。このような場合にファクトリメソッドパターンを使うと柔軟に対応することができます。例として、製品IDから製品名称を取得する場合を考えてみましょう。次の前提条件があるものとします。・開発の最終段階では、データベースに登録されている製品情報から製品名称、を検索する・データベースの設計やセッティングが完了していないので、現時点ではデータベースを利用できない。そのため、一時的にファイルを簡易データベースとして使用しながら開発を進めておきたい・後でファイル利用からデータベース利用へと簡単に修正できるようにしたい 72
  • データベースが完成するまでは、ファイルからデータを取得するクラス(FileDataObject)を代わりに使うことにします。 73
  • このクラスを呼び出すクライアント(main関数)では、単純にnewを使ってFileDataObjectクラスのオブジェクトを生成し、そのオブジェクトを使用します。 74
  • データベースが完成し、データベースを利用するクラス(仮にDBDataObjectとします)が使用できるようになれば、FileDataObjectオブジェクトの生成処理をDBDataObjectオブジエクトの生成処理に書き換えます。これをクラス図で示すと図のようになります。この図には、将来切り替える予定のDBDataObjectクラスも入れておきます。FileDataObjectとDBDataObjectは同じ機能を持っていますが、現在は2つの間に関連性はありません。 75
  • DataObjectクラスは、次のことを実現するためのクラスです。・クライアント側が直接FileDataObjectの存在を知らなくても良いようにする・FileDataObjectという具象クラスを明示したオブジェクト生成処理を1箇所だけに記述する 76
  • このDataObjectクラスのポイントは次のとおりです。・クラスメソッドcreate() の中だけでFileDataObjectの生成処理を行っている・create( )を呼び出した側 (main関数)は、このメソッドによって生成されたオブジェクトをDataObject型として受け取るので、それがFileDataObjectであるかどうかを意識する必要がない・getName()は純粋仮想関数として定義されており、実際の処理を含んでいないので、main関数がgetName()を呼び出したときには、DataObjectを継承したFileDataObjectクラス内のgetName()が実行される将来的にFileDataObjectクラスを置きかえるDBDataObjectクラスも、同じようにDataObjectクラスを継承するようにします。FileDataObjectクラスとDBDataObjectクラスの両方が同じDataObjectクラスを継承するところが、このパターンの要です。main関数側では、新たにオブジェクトを生成するときに、パターン未適用時のようにFileDataObjectクラスを直接インスタンス化するのではなく、DataObjectのクラスメソッドcreate( )を呼び出します。その後は、create()メソッドから返されたオブジェクトを使用します。 77
  • ファクトリメソッドパターンの目的は、クライアントへの影響を最小限に抑えつつ、クラスの切り替えを容易に行えるようにすることです。そこで、前述の例で実際にクライアントへの影響が抑えられているかどうかを確認してみましょう。パターン適用前とパターン適用後でクライアントコードがどのように変化しているかを見比べてください。違うのはオブジェクトを生成している箇所だけです。・パターン未適用:FileDataObject* pDataObject = new FileDataObject();・パターン適用後:DataObject* pDataObject = DataObject::create();パターン適用前とパターン適用後の違いをまとめると、次のようになります。◆パターン未適用時・newを使用してオブジェクトを取得する・ファイルを利用するクラス(FileDataObject)の名前をクライアント側で明示的に指定する必要がある◆パターン適用時・メソッドの呼び出しでオブジェクトを取得する・クライアント側では、ファイルを利用するクラス(FileDataObject)なのか、データベースを利用するクラス(DBDataObject)なのかを意識する必要がない 78
  • ファクトリメソッドパターンを適用した場合にクラスの切り替えがどのくらい容易になるかを具体的に考えてみましょう。パターン適用前のサンプルとパターン適用後のサンプルで、ファイル利用クラス(FileDataObject)をデータベース利用クラス(DBDataObject)に切り替えてみます。◆パターン未適用時クライアントコードでnewを使ってFileDataObjectオブジェクトを生成している箇所を修正します。複数の箇所で、FileDataObjectオブジェクトを生成している場合には、そのすべての箇所を探して修正する必要があります。◆パターン適用時クライアントコードを修正する必要はありません。複数の箇所でFileDataObjectオブジェクトを生成している場合でも、ファクトリメソッド(DataObjectクラスのcreate()メソッド)内でオブジェクト生成処理を行っている1箇所を修正するだけです。 79
  • 前記のファクトリメソッドは、引数がなく、決められたオブジェクトを返すだけでした。このままではクライアントの要求に応じて適切なオブジェクトを生成することができません。そこで、どのオブジェクトを生成するかという情報をクライアントから引数として渡すことにします。DataObjectクラスでたとえば定数を追加します。const static int STANDALONE = 0; //追加const static int NETWORKING = 1; //追加DataObject::create()を引数(int type)をとるように変更し、if (type ==STANDALONE) result = new FileDataObject();if (type == NETWORKING) result = new DBDataObject();とするとします。この例では、STANDALONEとNETWORKINGという定数を使用して、ネットワークに接続していない場合と接続している場合を区別しています。create()メソッドがSTANDALONEを受け取ったときはFileDataObjectオブジェクトを作成し、NETWORKINGを受け取ったときはDBDataObjectオブジェクトを作成します。これでうまくいくように見えますが、実はこの方法では、クライアントがDataObjectをいくつも生成する場合にはnewをしているのと変わらなくなってしまいます。ここではオブジェクト生成の責任をcreate()という「メソッド」だけで果たそうとしていますが、メソッドでは対応しきれないので、より大きな責任を負うことのできる「クラス」でオブジェクト生成を行うようにします。このクラスの名前をDataObjectFactoryとします。 80
  • DataObjectFactoryクラスでは、DataObjectFactoryの生成時にどのタイプのオブジェクトを生成するかを一度だけ指定します。それ以降は、create()メソッドを呼び出すだけで、再びタイプを指定せずに何度でもオブジェクトを生成できるようになります。 81
  • 82
  • 83
  • 84
  • ◆アブストラクトファクトリーのメリットパターン適用後の設計には、次のようなメリットがあります。・クライアントから具体的なクラスを隠蔽するクライアントは部品オブジェクトの具体的なクラスを意識する必要がありません。抽象部品クラスのインターフェースを介して部品オブジェクトにアクセスするので、具体的な部品クラスが変わっても、呼び出し側を変更する必要がありません。・部品をグループ化する一緒に使用すべき部品の生成処理をひとまとめにすることができます。その結果、一緒に使用すべきでない部品どうしを誤って組み合わせることがなくなります。・部品のグループを容易に変更できるクライアント側は抽象的な生成方法と部品だけを意識するので、どのセットを生成するかを簡単に変更できます。 85
  • ◆アブストラクトファクトリーの構成アブストラクトファクトリーはその名のとおり「抽象的な工場」を設けます。このパターンでは工場とそこで作る部品を抽象化し、具体的な生成方法と部品をクライアント側から隠蔽します。その結果、クライアント側は具体的な生成方法と部品を意識せず、どのような種類の部品でも同じように対応できるようになります。抽象化した部品はインターフェースとして定義します。各インターフェースは継承することを前提としていますので、メソッドはVirtual宣言を行い、さらに実体は子クラスで実装するため純粋仮想関数として宣言します。次に、具体的な部品クラスをこれらのクラスを実装して定義します。次に、抽象的な部品を生成する工場のクラスを定義します。これもVirtual宣言を行い、インターフェースとして定義します。これは抽象的な部品オブジェクトを生成するメソッドを定義したクラスです。この段階では具象クラスがどんな部品なのかということは関知せず、組み合わせるべき抽象化された部品を生成するメソッドを定義します。さらにインターフェースを実装して、具体的な部品ごとに、作成するファクトリークラスを定義します。 86
  • ◆テンプレートメソッドのメリット・流れを把握しやすい処理の流れはテンプレートメソッドで定義されているので、テンプレートメソッドを見れば大筋の流れがすぐにわかります。また、たとえば3パターンの処理をテンプレートメソッドから継承した場合、それぞれが1つの同じ流れであることがわかるので、それぞれを別々に理解する手間が省けます。テンプレートメソッドを用していない場合には、ソースコードを丹念に読まなければ同じ流れで処理しているものが存在するかどうかを知ることができません。・カスタマイズポイントが明確であるテンプレートメソッドパターンを使用すると、メソッドのオーバーライドが強制されます。このオーバーライドを強制されるメソッドが、カスタマイズ可能な部分になります。逆に言えば、カスタマイズ時にはそれ以外の部分を考えなくてよいということです。これにより、詳細の実装を変えなければいけない箇所はどこで、考えなくてよい箇所はどこかを明雄に示すことができます。 87
  • ◆テンプレートメソッドの構成3つの処理の流れはほぼ同じですが、具体的な処理内容は異なります。このように、処理の大筋の流れは一緒でも個別の処理は異なる、というケースでテンプレートメソッドが役に立ちます。上記スライドの処理は、いずれも以下の手順で行われています。1.ファイルからデータを読み込む2. データを整形する3.整形したデータを出力するこの流れをFileFormatterという抽象クラスで定義します。仮にformatter()というメソッドをテンプレートメソッドとして定義します。このメソッドが1から3の処理の流れを定義(=テンプレートを提供)しています。テンプレートメソッドの中で呼び出すメソッドは、基本的には純粋仮想関数として定義しておきます。こうすることで、このクラスを継承したサブクラス側にメソッドのオーバーライドを強制することができます。具体的な処理は、このFileFormatter抽象クラスを継承したサブクラス内で実装します 88
  • ◆イテレータパターンのメリットイテレータパターンを適用すると、集約オブジェクト内のオブジェクトに順次アクセスするときに、次の決まった手順を使うことになります。1. Iteratorオブジェクトをiterator()メソッドで取得する2. IteratorオブジェクトのhasNext()メソッドで次のオブジェクトが存在するか確認する3. 次のオブジェクトが存在すれば、Iteratorオブジェクトのnext()メソッドで次のオブジェクトを取得するこのようにパターンを適用した場合は、Machineクラスを変更しでも、Iteratorオブジェクトによる統一化されたメソッドを利用している限り、クライアント側のコードはいっさい変更する必要がありません。 89
  • 集約オブジェクトに格納されるオブジェクトはPartsで、これを複数保持して集約しているのがMachineクラスです。Machineクラスはオブジェクトへの順次アクセスを統一的に行うためにAggregateインターフェースを実装し、iterator()を実装します。実際に順次アクセスを行うのはIteratorMachineクラスです。IteratorMachineクラスは順次アクセスの手順を統一化するために、hasNext()とnext()を宣言しているIteratorインターフェースを実装しています。 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 基本的な流れは次のようになっています。1.socket関数リクエストを受け付けるためのソケットを作成します。2.bind関数サーバーのアドレス情報(IPアドレス及びポート番号)をソケットに結び付けます。3.listen関数リクエストの接続待ちキューを設定し、接続要求の準備をします。4.accept関数接続待ちのリクエストを受付けて接続を確立し、新しいソケットを作成します。accept関数により新しく生成されたソケットは、 通信専用のソケットで、send関数及びrecv関数によりデータの送受信ができます。 これらの関数は、クライアントアプリケーションでも使われるので、後程まとめて解説します。 108
  • socket関数の使い方TCPのソケットを生成するときは、通常次のような記述をします。SOCKET s=socket(AF_INET,SOCK_STREAM,0);if(s==INVALID_SOCKET){ printf("ソケットを生成できません"); exit(1);} /* 以下、bindへ続く */ソケットの生成は以上で完了です。 109
  • bind関数の使い方bindを使ってソケットにアドレス情報を結び付けるには次のような記述をします。 SOCKADDR_IN sain; sain.sin_family=AF_INET; sain.sin_addr.s_addr=INADDR_ANY; sain.sin_port=htons(port_number); if(bind(s,(SOCKADDR*)&sain,sizeof(sain)!=0){ printf("バインドに失敗しました"); exit(1); } /* 以下、listenへ続く */ソケットへアドレス情報を結び付ける作業は以上で終了です。 なお、上で出てきたhtons関数はコンピュータの数値表現をネットワークの数値表現に変更するものです。 リトルエンディアンをビッグデンディアンに変更します。 110
  • listen関数の使い方 if(listen(s,10)!=0){ printf("listenに失敗しました。"); exit(1); } /* 以下、accept へ続く */ 111
  • accept関数の使い方 SOCKET t; SOCKADDR_IN ta; int ta_len; ta_len=sizeof(ta); t=accept(s,&ta,&ta_len); if(t==INVALID_SOCKET){ printf("接続が確立できませんでした。"); exit(1); } /* 以下、send関数及びrecv関数を使って送受信操作ができる */接続待ちキューにリクエストがなかった場合、 accept関数を実行するとプログラムはここでブロックします。そして接続要求があると、ここから動作を再開します。 112
  • 普通の状態では、recvやrecvfromはデータが受信できるまでブロッキングします。 ソケットを一つしか利用していない場合にはブロッキングは非常に便利なのですが、ソケットが複数になると困ってしまいます。 複数のソケットを扱うとき、片方のソケットでブロッキングしたままになってしまうと他のソケットにデータが到着しても受信が出来なくなってしまいます。 113
  • 114
  • 115
  • 116
  • connect関数の使い方inet_addr関数は、IPアドレスをIN_ADDR構造体に適したアドレス形式に変換します。gethostbyname関数はDNSサーバーに問い合わせて、ドメインからIPアドレスを取得する操作を行います。gethostbyname関数を実行するとhostent構造体へのポインタが返ります。 この構造体の中に接続先のIPアドレスがあるので、これを利用してアドレス情報を作成します。#include <stdio.h>#include <winsock2.h>int main( void ){ int sock; struct sockaddr_in addr; int ret ; struct hostent *hostinfo; unsigned long inetaddress; char *hostname = "localhost"; WSADATA wsadata; WSAStartup( 0x0002, &wsadata ); inetaddress = inet_addr( hostname ); if ( inetaddress == INADDR_NONE ) { hostinfo = gethostbyname( hostname ); if ( hostinfo == 0 ) { return -1; }// ホスト名解決に失敗 inetaddress = *(unsigned long *)hostinfo->h_addr_list[0]; } addr.sin_family = AF_INET; addr.sin_addr.s_addr = inetaddress; addr.sin_port = htons(7000); sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); ret = connect( sock, (struct sockaddr *)&addr, sizeof addr ); if ( ret < 0 ) { printf( "localhost 7000 に接続できなかった" ); return 0; } printf( "localhost 7000 に接続できた" ); closesocket( sock ); return 1;} 117
  • send関数の使い方文字列"Hello¥r¥n"を送信する場合は次のようになります。 int ok; const char* st="Hello¥r¥n";ok=send(s,st,7,0);if(ok==SOCKET_ERROR){ printf("送信できませんでした。"); exit(1);} 118
  • recv関数の使い方recv関数は受信長が必ずしもlenまで達しているとは限りません。 そこで、recv関数を使って、lenまで受信する関数receiveを紹介します。int receive(SOCKET s,char* buf,int* len){ int revd_size; int tmp; revd_size=0; while(revd_size<*len){ tmp=recv(s,buf+revd_size,*len-revd_size,0); if(tmp==SOCKET_ERROR){ /* エラーが発生 */ *len=received; return SOCKET_ERROR; } if(tmp==0){ /* ソケットが切断された */ *len=received; return 0; } received+=tmp; } *len=received; return received;}このreceive関数は、lenまで受信するかソケットが閉じられるまでrecv関数を呼び続けます。 返り値はrecv関数と同じです。しかし、読み込みの途中でソケットが閉じられた場合を想定して、 引き数lenはポインタとし、ここに受信したデータ長を返すようにしてあります。 119
  • ws2_32.libは$(WindowsSdkDir)¥ws2_32.libに存在しています。指定はws2_32.libでかまいません。プロジェクトのプロパティ>構成プロパティ>リンカ>入力>追加の依存ファイルで設定を行います。 120
  • WSAStartupでWinsockのライブラリのバージョン指定をしますMAKEWORD(2,0)はWinsock2.0を使うことを示しますWinsockライブラリを読み込むことで以降のソケット関係の関数を使用することができます 121
  • 122
  • 123
  • 124
  • SQLiteのサイトからソースコードおよびコンパイル済のDLLが入手可能通常はアプリケーションで利用するにはソースコードをコンパイルしライブラリを作らなければならないダウンロード・ページから、 sqlite3_amalgamation-####.zip(ソースコード、ヘッダ、DEFファイル) sqlite3_dll-####.zip(DLL)の2つを入手します(####にはバージョン番号が付加されています)得られたDLLとDEFからインポート・ライブラリ(LIB)を生成しましょう。「sqlite3.dll」と「sqlite3.def」を適当なディレクトリに置き、コマンドプロンプトから、lib /VERBOSE /MACHINE:I386 /DEF:sqlite3.def /OUT:sqlite3.lib /NAME:sqlite3.dll を実行することでインポート・ライブラリ「sqlite3.lib」が生成されます。ユーザーコードのコンパイル時に「sqlite3.h」、リンク時に「sqlite3.lib」、実行時に「sqlite3.dll」が使われることになります。 125
  • SQL文に記述された値の記述方法からその値のデータ型を決めています。例えば「」で囲まれていればTEXT型、小数点も指数も無ければINTEGER型、小数点か指数があればREAL型、などです。 126
  • 127
  • 128
  • 上記のように、データベース・ファイルのパスとデータベース・ハンドル(sqlite3*)のポインタを与えます。指定したファイルが存在しないときは新規に作成されます。データベース・ファイルパスに":memory:"を指定するとデータベースがメモリ内に作られ、アプリケーション実行時にのみ有効な揮発性データベースとして使えます。戻り値は処理の結果を表し、SQLITE_OKであれば成功、失敗時にはエラーコードが返されます。最後に呼び出された関数の結果に対応するエラーメッセージはconst char* sqlite3_err_msg()で取得できます。sqlite3* db;int result = sqlite3_open(":memory:", &db);if ( result != SQLITE_OK ) { cerr << sqlite3_errmsg() << endl; return;}◆open引数filenameは、sqlite3_open()とsqlite3_open_v2()ではUTF-8、sqlite3_open16()ではUTF-16として、ネイティブのバイトオーダーで解釈されます。・データベースを(生成して)開くことに成功した場合、SQLITE_OKが返されます。・それ以外の場合はerror codeを返します。・データベースのデフォルトのエンコーディングは、sqlite3_open()とsqlite3_open_v2()を呼び出す場合はUTF-8、sqlite3_open16()を使用する場合はネイティブのバイトオーダーでUTF-16になります。注意:現在定義されているコードページに関わらず、sqlite3_open()とsqlite3_open_v2()の引数filenameに使用するエンコーディングはUTF-8にする必要があります。ファイル名が国際文字を含む場合は、sqlite3_open()またはsqlite3_open_v2()に渡す前にUTF-8に変換する必要があります。 129
  • ◆sqlite3_open_v2()sqlite3_open_v2()インターフェイスは、新しいデータベース接続を介して追加制御のために2つのパラメータを追加を受け取る以外は、sqlite3_open()のように動作します。・sqlite3_open_v2()のフラグパラメータは下記の3つの内の1つを指定し、オプションでSQLITE_OPEN_NOMUTEX、SQLITE_OPEN_FULLMUTEX、SQLITE_OPEN_SHAREDCACHE、SQLITE_OPEN_PRIVATECACHEフラグを結合します。◎SQLITE_OPEN_READONLYデータベースを読み取り専用モードで開きます。データベースが既に存在しない場合はエラーを返します。◎SQLITE_OPEN_READWRITEデータベースを読み書き可能モードで開きます。オペレーティングシステムによってファイルが書き込み保護されている場合は読み取り専用として開きます。どちらの場合もデータベースが既に存在している必要があり、存在しない場合はエラーを返します。◎SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATEデータベースを読み書き可能モードで開き、既に存在しない場合は生成します。これはsqlite3_open()とsqlite3_open16()のデフォルトの動作です。・追加のフラグ◎SQLITE_OPEN_NOMUTEXフラグを設定した場合、コンパイル時または起動時にシングルスレッドモードに設定されていない限り、threading modeをマルチスレッドでデータベース接続を開きます。◎SQLITE_OPEN_FULLMUTEXフラグを設定した場合、コンパイル時または起動時に予めシングルスレッドを選択していない限り、threading modeをシリアライズでデータベース接続を開きます。◎SQLITE_OPEN_SHAREDCACHEフラグは、sqlite3_enable_shared_cache()を使用して共有キャッシュが有効になっているかどうかに関わらず、shared cache modeを使用して適格なデータベース接続をもたらします。◎SQLITE_OPEN_PRIVATECACHEフラグは、たとえshared cache modeが有効であっても、共有しないデータベース接続をもたらします。・4番目のパラメータは、データベース接続が使うであろう、オペレーティングシステムのインターフェイスを定義したsqlite3_vfsオブジェクトの名前になります。NULL指定の場合は、デフォルトのsqlite3_vfsが使用されます。 129
  • SQLの実行は大きく2つのフェーズ「prepare」と「step」に分かれます。「prepar」ではSQL文をあらかじめコンパイル(構文解釈)し、実行に備えます。zSqlに与えたSQL文がコンパイルされ、ステートメント・ハンドル(sqlite3_stmt*)が*ppStmtに出力されます。nByteは文字数ではなくバイト数であることに留意してください(-1とすればSQL文が¥0終端されているとみなします)。SQLの実行はステートメント・ハンドルに対して行われます。 130
  • ステートメント・ハンドルの廃棄を怠ると、リソースリークによりプログラムの動作に影響が発生します。 131
  • sqlite3_prepare_v2の定義は以下の通りです。int sqlite3_prepare_v2( sqlite3 *db, /* データベースハンドル*/ const char *zSql, /* SQLステートメント(UTF-8エンコード) */ int nByte, /* zSqlの文字列長 */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */);const char* command = "DROP TABLE price_list;" "CREATE TABLE price_list ( item TEXT, price INTEGER);";この処理は1つの文字列に複数のSQLを含めています。 132
  • ◆?NNN-- SQLINSERT INTO tower ( name, height ) VALUES ( ?111, ?222 );/* API */sqlite3_bind_text( stmt, 111, "東京スカイツリー", -1, SQLITE_STATIC );sqlite3_bind_int( stmt, 222, 634 );-- 実行される処理INSERT INTO tower ( name, height ) VALUES ( 東京スカイツリー, 634 );プレースホルダが?番号のときは、sqlite3_bind関数で指定した位置はその番号とする。番号は1から999(デフォルト)の間でなければならない。上の例では、プレースホルダが?111, ?222なので、sqlite3_bind_textの引数では111、sqlite3_bind_intの引数では222を指定している。◆@VVVプレースホルダは記号+名称で設定しても良い。記号は:, @, $のいずれかを使用する。ただしパラメータの位置を設定する引数はint型のため、パラメータの名称からSQL文中の位置を割り出す関数が必要になる。その際にsqlite3_bind_parameter_indexを使用する。これはプリペアードステートメントとプレースホルダを引数とし、見つかればその位置を戻り値とする。位置が見つからないときの戻り値は0で、これはパラメータの位置としては無効な値となる。注:同じ名称のプレースホルダが1つのSQL文中に複数含まれているときは、それぞれの箇所に同じ値が設定される。記号+名称のとき(例) 133
  • -- SQLINSERT INTO tower ( name, height ) VALUES ( @name, @height );/* API */sqlite3_bind_text( stmt, sqlite3_bind_parameter_index( stmt, "@name" ), "東京スカイツリー", -1, SQLITE_STATIC );sqlite3_bind_int( stmt, sqlite3_bind_parameter_index( stmt, "@height" ), 634 );-- 実行される処理INSERT INTO tower ( name, height ) VALUES ( 東京スカイツリー, 634 ); 133
  • 引数は以下のとおりです。1. ステートメント・ハンドル2. プレースホルダの位置(最初のプレースホルダを1とする)3. バインド値4. バインド値のバイト数5. デストラクタ……実行完了時にバインド値を引数として呼び出される関数へのポインタ。バインド値 が実行中に変化しないのであればSQLITE_STATIC、バインド値のコピーをステートメントが保持す るならSQLITE_TRANSIENTを指定できます。 134
  • 135
  • SQL文がSELECTのように複数(0個以上)の結果を返すとき、sqlite3_step()を何度も呼び出すことでその結果が得られますが、sqlite3_step()は結果の個数だけSQLITE_ROW、そして最後にSQLITE_DONEを返します。結果の取得は、sqlite3_column_*()にステートメント・ハンドルと何列目のカラムか(0起点)を指定することで得られます。これらのルーチンは、クエリの現在の結果行の単一列に関する情報を返します。◆引数全ての関数の第1引数は評価されるprepared statementのポインタ(sqlite3_prepare_v2()から返されるsqlite3_stmt*)で、第2引数は取得したい列のインデックスです。・結果セットの左端の列のインデックスは0です。・結果セットの列数は、sqlite3_column_count()を使用して決定することができます。・SQLステートメントが現在有効な行を指していない、または列のインデックスが範囲外の場合、結果は未定義になります。・これらのルーチンは、最新のsqlite3_step()の呼び出しでSQLITE_ROWが返された時のみ呼び出されます。・列の型を確認する必要がある場合、sqlite3_column_type()ルーチンを使用すると、データ型のコードが返されます。戻り値は、SQLITE_INTEGER、SQLITE_FLOAT、SQLITE_TEXT、SQLITE_BLOB、またはSQLITE_NULLのいずれかになります。◎結果がBLOBまたはUTF-8文字列の場合、sqlite3_column_bytes()によってバイト数を取得できます。◎結果が数値の場合、sqlite3_column_bytes()はsqlite3_snprintf()によりUTF-8文字列に変換され、文字列のバイト数を返します。◎結果がNULLの場合、sqlite3_column_bytes()はゼロを返します。◎sqlite3_column_bytes()によって返される値は、文字列の末端にあるゼロ終端を含みません。◎sqlite3_column_text()によって返される文字列は空の文字列と同様に常にゼロ終端です。 136
  • 137
  • ◆sqlite3_column_type()を用いたsqlite3_column_*()の実行sqlite3_column_type()の結果をswitchで場合分けし、それに応じてデータ型ごとのsqlite3_column_*()が実行される。 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • CREATE TABLE EMP( EMPNO NUMBER(4,0) NOT NULL, ENAME VARCHAR2(10), JOB VARCHAR2(9), MGR NUMBER(4,0), HIREDATE DATE, SAL NUMBER(7,2), COMM NUMBER(7,2), DEPTNO NUMBER(2,0), PRIMARY KEY (EMPNO));CREATE TABLE DEPT( DEPTNO NUMBER(2,0), DNAME VARCHAR2(16), LOC VARCHAR2(10), PRIMARY KEY (DEPTNO)); 167
  • INSERT INTO DEPT (DEPTNO,DNAME,LOC) VALUES(10,ACCOUNTING,NEW YORK);INSERT INTO DEPT (DEPTNO,DNAME,LOC) VALUES(20,RESEARCH,DALLAS);INSERT INTO DEPT (DEPTNO,DNAME,LOC) VALUES(30,SALES,CHICAGO);INSERT INTO DEPT (DEPTNO,DNAME,LOC) VALUES(40,OPERATIONS,BOSTON);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7369,SMITH,CLERK,7902,1980-12-17,800,,20);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7499,ALLEN,SALESMAN,7698,1981-02-20,1600,300,30);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7521,WARD,SALESMAN,7698,1981-02-22,1250,500,30);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7566,JONES,MANAGER,7839,1981-04-02,2975,,20);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7654,MARTIN,SALESMAN,7698,1981-09-28,1250,1400,30);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7698,BLAKE,MANAGER,7839,1981-05-01,2850,,30);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7782,CLARK,MANAGER,7839,1981-06-09,2450,,10);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7788,SCOTT,ANALYST,7566,1987-04-19,3000,,20);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7839,KING,PRESIDENT,,1981-11-17,5000,,10);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7844,TURNER,SALESMAN,7698,1981-09-08,1500,0,30);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) 168
  • VALUES(7876,ADAMS,CLERK,7788,1987-05-23,1100,,20);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7900,JAMES,CLERK,7698,1981-12-03,950,,30);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7902,FORD,ANALYST,7566,1981-12-03,3000,,20);INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)VALUES(7934,MILLER,CLERK,7782,1982-01-23,1300,,10); 168
  • SELECT ENAME,SAL FROM EMP WHERE SAL = (SELECT SAL FROM EMP WHERE ENAME = SCOTT);SELECT deptno,ename,sal FROM emp WHERE sal = (SELECT MAX(sal) FROM emp); 169
  • sqliteでは明示的なトランザクションを実行すると性能が向上します。◆ロールバックする例BEGIN TRANSACTION;INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) VALUES(1112,MIKU,CLERK,7902,2009-09-30,800,NULL,20);ROLLBACK;SELECT * FROM EMP;7369|SMITH|CLERK|7902|1980-12-17|800||20(中略)※MIKUは表示されない◆コミットできる例BEGIN TRANSACTION trn1;INSERT INTO EMP (EMPNO,ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO) VALUES(1112,MIKU,CLERK,7902,2009-09-30,800,NULL,20);COMMIT TRANSACTION trn1;SELECT * FROM EMP;(中略)1112|MIKU|CLERK|7902|2009-09-30|800||20 170