スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

計算機の基本原理を味わおう21 - オブジェクトの役割をきっちり決めて結合度を下げよう

 この記事は、計算機の基本原理を味わおう20 - 状態遷移と停止問題について考えようの続きです。前回は、停止問題について解説しました。今回は、オブジェクトの結合度に関する事柄を解説します。
 サンプルコードの量が増えてきたので、オブジェクト指向プログラミングをする上で避けては通れない、オブジェクトの結合度について考えてみましょう。オブジェクトの結合度というのは、簡潔に表現すると、オブジェクトがどれだけ他のオブジェクトを参照しているか、もしくは参照されているのかを表す概念です。この連載で扱っているサンプルコードは、まだ大した量ではありませんが、コード量が少ない段階から考えていくほうがよいです。何故ならば、コード量が多くなってから結合度について考えると、構造が把握し難く失敗しやすいからです。本来はオブジェクト指向設計で考えるのが良いのですが、プログラミングしている段階でも考えるべきです。
 具体的に考えていきます。最少計算機で気になるのは、CompareMoveオブジェクト(比較移動命令)の結合度が高い点です。現在の実装はメモリの読み書きに限定されていますが、比較移動本来の動きは、メモリに限ったものではありません。本来するべきことは、値を移動(コピー)するだけなので、メモリ以外でも実行できるようにしたほうがよいといえます。
 オブジェクトの結合度を低くするためには、インタフェースを定義し、それを参照するのがよいでしょう。

using System;

namespace MiniBitMachine.V3
{
    //命令の値(オペランド)が持つべきインタフェースの定義。
    public interface IOperand
    {
        ushort Value { get; set; }
    }
}

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace MiniBitMachine.V3
{
    //比較移動命令。
    public class CompareMove : 
        Instruction, 
        IEquatable
    {
        //関係のないメンバーは省略

        //命令長。
        public static int Length { get { return 16; } }

        //比較フラグ
        private bool _compareFlag;
        public bool CompareFlag
        {
            get { return this._compareFlag; }
            set { this._compareFlag = value; }
        }

        //宛先。
        private IEnumerable _destinationBinary;
        private IOperand _destination;
        public IOperand Destination
        {
            get { return this._destination; }
            internal set { this._destination = value; }
        }

        //送信元。
        private IEnumerable _sourceBinary;
        private IOperand _source;
        public IOperand Source
        {
            get { return this._source; }
            internal set { this._source = value; }
        }

        //インスタンスを生成する。
        public CompareMove() : this( null, null, "" )
        {
        }

        //宛先と送信元を指定してインスタンスを生成する。
        public CompareMove( IOperand destination, IOperand source )
            : this( destination, source, "" )
        {
        }

        //宛先と送信元とコメントを指定してインスタンスを生成する。
        public CompareMove(
            byte destination,
            byte source,
            string comment )
        {
            this._destinationBinary =
                BitMaster.ConvertBinary( destination );
            this._destination = new Immediate( destination );
            this._sourceBinary =
                BitMaster.ConvertBinary( source );
            this._source = new Immediate( source );
            this.Comment = comment;
        }

        //宛先と送信元とコメントを指定してインスタンスを生成する。
        public CompareMove( 
            IOperand destination, 
            IOperand source, 
            string comment )
        {
            if ( destination != null ) 
                this._destinationBinary =
                    BitMaster.ConvertBinary( ( byte ) destination.Value );
            this._destination = destination;
            if ( source != null )
                this._sourceBinary =
                    BitMaster.ConvertBinary( ( byte ) source.Value );
            this._source = source;
            this.Comment = comment;
        }

        //バイナリ値からインスタンスを生成する。
        public CompareMove( IEnumerable binary )
        {
            if ( binary.Count( ) != CompareMove.Length )
                throw new ArgumentException(
                    String.Format(
                        "必ず{0}ビットの値を指定してください。",
                        CompareMove.Length ) );
            this._destinationBinary =
                binary.Where(
                    ( b, index ) =>
                        ( index < 8 ) ).Select( b => b );
            this._destination = new Immediate( 
                BitMaster.ConvertToByte( this._destinationBinary ) );
            this._sourceBinary =
                binary.Where(
                    ( b, index ) =>
                        ( index >= 8 ) ).Select( b => b );
            this._source = new Immediate( 
                BitMaster.ConvertToByte( this._sourceBinary ) );
        }

        //命令を実行する。
        public virtual void Execute()
        {
            if ( this._compareFlag )
                this.Destination.Value = this.Source.Value;
        }

    }
}

これで値を持つオブジェクトに対して、比較移動を使用できるようになりました。比較移動命令を変更すると、実行部分を変更する必要が生じるのでます。その方法として、実行を担当するExecuterオブジェクトを変更する手もありますが、それでは実行と解析の役割を兼用することになってしまいます。何故ならば、インタフェースを実装するオブジェクトを設定するコードドが必要になるからです。
 では、どうすればよいのでしょうか?こんな時は、オブジェクトが持つ役割は1つだと考えるとよいでしょう。ですから今回ベストな方法は、解析を担当するDecoderオブジェクトを定義することだと思います。実装なこんな具合になります・・・

using System;
using System.Collections.Generic;

namespace MiniBitMachine.V3
{
    //命令を解析するオブジェクト。
    public class Decoder
    {
        //実行に必要な情報。
        ExecuteEnvironment _exeInfo;

        //アドレスを管理するオブジェクト。
        Addresser _addresser;

        //解析に必要な情報を指定し、
        //インスタンスを生成する。
        public Decoder(
            ExecuteEnvironment exeInfo, 
            Addresser addresser )
        {
            this._addresser = addresser;
            this._exeInfo = exeInfo;
        }

        //命令をデコードする。
        public CompareMove Decode( IEnumerable<bool> binary )
        {
            CompareMove temp = new CompareMove( binary );
            CompareMove result = this.SetOperand( temp );
            return result;
        }

        //オペランドを設定する。
        private CompareMove SetOperand( CompareMove command )
        {
            if ( command.Destination.Value ==
                this._exeInfo.CompareFlagAddress ) {
                    return SetCompareFlagOperand( command );
            } else if ( command.Destination.Value ==
                    this._exeInfo.ProgramCounterAddress ) {
                        return SetProgramCounterOperand( command );
            } else if ( command.Destination.Value ==
                this._exeInfo.LastAddress ) {
                    return SetStopCpuOperand( command );
            } else {
                return SetMemoryOperand( command );
            }
        }

        //比較フラグに関するオペランドを用意する。
        private CompareMove SetCompareFlagOperand( CompareMove command )
        {
            command.CompareFlag = true;
            command.Destination = this._exeInfo.CompareFlag;
            command.Source = this.GetSource( command );
            return command;
        }

        //プログラムカウンタに関するオペランドを用意する。
        private CompareMove SetProgramCounterOperand( CompareMove command )
        {
            command.CompareFlag = this._exeInfo.GetCompareFlag();
            command.Destination = this._addresser.ProgramCounter;
            return command;
        }

        //CPU停止に関するオペランドを用意する。
        private CompareMove SetStopCpuOperand( CompareMove command )
        {
            command.CompareFlag = false;
            this._exeInfo.IsStop.Value = 1;
            command.Source = this.GetSource( command );
            return command;
        }

        //メモリに関するオペランドを用意する。
        private CompareMove SetMemoryOperand( CompareMove command )
        {
            MemorySnapShot destination = 
                new MemorySnapShot( 
                    this._addresser, 
                    this._exeInfo.ReferenceMemory, 
                    command.Destination.Value );
            command.CompareFlag = this._exeInfo.GetCompareFlag( );
            command.Destination = destination;
            command.Source = new Immediate( command.Source.Value );
            return command;
        }

        //メモリ用のソースを取得する。
        private IOperand GetSource( CompareMove command )
        {
            IOperand result = new MemorySnapShot(
                    this._addresser,
                    this._exeInfo.ReferenceMemory,
                    command.Source.Value );
            return result;
        }

    }
}

さらにやるべきことがあります。それは、レジスタオブジェクトと、メモリの一時状態(スナップショット)が必要です。なぜならば、IOperandインタフェースを実装しないと、比較移動命令に指定できないからです。
 以上のように、オブジェクトの結合度を低く保つには、ある程度の作業と新しいオブジェクトが必要となります。面倒だと思う人もいるでしょうが、結合度が高いオブジェクトを保守することを考えれば、無視できるコストです。必要ならば、一時的な労力を惜しんではなりません。プログラミングでは、将来のコストを下げるために、今コストがかかってもするのが原則です。覚えておくとよいと思います。続く...
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

インドリ

Author:インドリ
みなさん、はじめまして、
コンニチハ。

ボクは、無限の夢(infinity dream)を持つネタ好きな虹色の鳥インドリ(in dre)です。
色々な情報処理技術を啄ばむから楽しみにしてね。

http://twitter.com/indori
は別人による嫌がらせ行為です。
私とは関係ないので注意して下さい。
次はなりすましブログなどをするかもしれませんが、ここ以外でブログをするつもりがないので、ここ以外にインドリのブログがあったとしても無視してください。


何度言っても分からない人がいるので、ここにコメント欄へ書き込むときの注意事項を書きます。


一、社会人としてのマナーをわきまえましょう。
一、妄想に基づく書き込みを止めてください。
一、暴言の類は書かないで下さい。
一、某誹謗中傷サイトの書き込みは彼らの妄想に基づく書き込みですから無視して、ここへ書き込まないで下さい。
一、コメント書く前に他のコメントよく読んでから行って下さい。
一、言いがかかり等の行為を禁止します。
一、その他常識的に考えて迷惑なコメントはしないで下さい。


以上のルールを守れない人のコメントは削除します。



利用上の注意
ここに紹介してある文章およびプログラムコードは正確であるように心がけておりますが、内容を保証するものではありません。当サイトの内容によって生じた損害については、一切の責任を負いませんので御了承ください。


執筆したCodeZineの記事


【VB.NETで仮想CPUを作ろう】

  1. VB.NETで仮想CPUを作ろう
  2. レジスタの実装
  3. 仮想CPUのGUI化
  4. テストドライバの改良
  5. CPUの基礎動作の実装
  6. MOV命令の実装
  7. ADD命令実装
  8. SUB命令実装
  9. INC命令&DEC命令の実装と命令長
  10. MLU命令の実装とModR/Mについて
  11. DIV命令の実装とイベント設計について
  12. 機械語駆動式 関数電卓を作ろう!
  13. 機械語駆動式 関数電卓を作ろう! 解答編(前半)
  14. 機械語駆動式 関数電卓を作ろう! 解答編(後半)


【仮想ネットワーク実装でTCP/IPを学ぼう】
  1. TCP/IPの基礎と勘所
  2. ネットワークアクセス層の勘所
  3. インターネット層の勘所
  4. トランスポート層の勘所
  5. アプリケーション層の勘所
  6. セキュリティの基礎と仮想ネットワークの仕様
  7. GDI+と独自プロトコルの定義



【並列化】
インテル Parallel Studioを使って並列化プログラミングを試してみた
並列プログラミングの効率的なデバッグを実現する「Parallel Inspector」


【TBBシリーズ】
  1. インテル スレッディング・ビルディング・ブロックの概要
  2. インテルTBBから学ぶループの並列化
  3. スレッドセーフとインテルTBBのコンテナ
  4. インテルTBBのスレッドクラス


【OpenMPシリーズ】
  1. OpenMPの基礎構文
  2. OpenMPの実行時ライブラリと並列ループ
  3. OpenMPのメモリモデルとfork- joinモデル

最近の記事
最近のコメント
月別アーカイブ
カテゴリ
Ada (9)
COBOL (5)
C (9)
C++ (11)
C# (370)
D (25)
Java (8)
Perl (1)
Ruby (14)
PHP (2)
Boo (2)
Cobra (2)
LISP (6)
F# (33)
HTML (0)
XHTML (0)
CSS (0)
XML (0)
XSLT (0)
Scala (4)
WPF (0)
WF (2)
WCF (0)
LINQ (4)
MONO (5)
Linux (0)
MySQL (0)
ブログ内検索
リンク
最近のトラックバック
RSSフィード
ブロとも申請フォーム

この人とブロともになる

QRコード
FC2カウンター
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。