オブジェクト指向の倒し方11/11 | 「オブジェクト指向の倒し方、知らないでしょ? オレはもう知ってますよ」

「オブジェクト指向の倒し方、知らないでしょ? オレはもう知ってますよ」

オブジェクト指向と公的教義の倒し方を知っているブログ
荀子を知っているブログ 織田信長を知っているブログ

<設計考察-多態(異態同化と同契機化) 2/2>
 
より動的な多態の自作の例を示す。

class DTypeA
{
public:
    int iDTypeA01;
    int iDTypeA02;
    int iDTypeA03;
    int iDTypeA04;
};
class DTypeB
{
public:
    int iDTypeB01;
    int iDTypeB02;
    int iDTypeB03;
};
class DTypeC
{
public:
    int iDTypeC01;
    int iDTypeC02;
};
class P_TypeA : DTypeA
{
public:
    void ProcA01()
    {
        iDTypeA01 = 100;
        iDTypeA02 = 200;
        iDTypeA03 = 300;
        iDTypeA04 = 400;
    }
    void ProcA02()
    {
        iDTypeA01 = 1000;
        iDTypeA02 = 2000;
        iDTypeA03 = 3000;
        iDTypeA04 = 4000;
    }
    void ProcAEnd()
    {
        iDTypeA01 = 0;
        iDTypeA02 = 0;
        iDTypeA03 = 0;
        iDTypeA04 = 0;
    }
};
 
class P_TypeB : DTypeB
{
public:
    void ProcB01()
    {
        iDTypeB01 = 2001;
        iDTypeB02 = 2002;
        iDTypeB03 = 2003;
    }
    void ProcB02()
    {
        iDTypeB01 = 5001;
        iDTypeB02 = 5002;
        iDTypeB03 = 5003;
    }
    void ProcBEnd()
    {
        iDTypeB01 = 0;
        iDTypeB02 = 0;
        iDTypeB03 = 0;
    }
};
 
class P_TypeC : DTypeC
{
public:
    void ProcC01()
    {
        iDTypeC01 = 123;
        iDTypeC02 = 456;
    }
    void ProcC02()
    {
        iDTypeC01 = 7890;
        iDTypeC02 = 1234;
    }
};

class CLASS{};
 
class Variable
{
public:
    void Start( int iMaxSize )
    {
        int iSizeMem  = sizeof( size_t ) * 5;
        BYTE *pbyMem = new BYTE[ iSizeMem ];
        size_t  *pCount   = ( size_t * )pbyMem;
        size_t  *pMax     = ( size_t * )pbyMem + 1;
        size_t **pCate    = ( size_t** )pbyMem + 2;
        void   **pList    = ( void  ** )pbyMem + 3;
        void   **pProc    = ( void  ** )pbyMem + 4;
 
        // 個数管理用
        *pCount = 0;
        *pMax   = iMaxSize;
 
        // 異態同化用
        *pCate = new size_t[ iMaxSize ];
        *pList = new  void*[ iMaxSize ];
 
        // 同契機化用
        *pProc = new void*[ 2 ];
        void ***pRefProc = ( void *** )*pProc;
        pRefProc[ 0 ] = new void *[ 4 ];
        pRefProc[ 1 ] = new void *[ 2 ];
 
        // 同契機化予定の手続きを設置
        int( CLASS::*pThisProc )();
        int iAddSize = sizeof( void * );
 
        // 多態1の対象手続き
        pThisProc = ( int( CLASS::* )() )&P_TypeA::ProcA01; memcpy( &pRefProc[ 0 ][ 0 ], &pThisProc, iAddSize );
        pThisProc = ( int( CLASS::* )() )&P_TypeB::ProcB01; memcpy( &pRefProc[ 0 ][ 1 ], &pThisProc, iAddSize );
        pThisProc = ( int( CLASS::* )() )&P_TypeC::ProcC01; memcpy( &pRefProc[ 0 ][ 2 ], &pThisProc, iAddSize );
        pThisProc = ( int( CLASS::* )() )&P_TypeC::ProcC02; memcpy( &pRefProc[ 0 ][ 3 ], &pThisProc, iAddSize );
 
        // 多態2の対象手続き
        pThisProc = ( int( CLASS::* )() )&P_TypeA::ProcAEnd; memcpy( &pRefProc[ 1 ][ 0 ], &pThisProc, iAddSize );
        pThisProc = ( int( CLASS::* )() )&P_TypeB::ProcBEnd; memcpy( &pRefProc[ 1 ][ 1 ], &pThisProc, iAddSize );
 
        void **pRetThis = ( void ** )this;
        pRetThis[0] = pbyMem;
    }
 
    void VariableAdd( void *pAdd, int iCate )
    {
        void *pThis = this;
        size_t  *pCount = ( size_t * )pThis;
        size_t  *pMax   = ( size_t * )pThis + 1;
        size_t  *pCate  = ( size_t * )pThis + 2;
        void   **pList  = ( void  ** )pThis + 3;
        void   **pProc  = ( void  ** )pThis + 4;
 
        size_t *pRefCate = ( size_t * )*pCate;
        void  **pRefList = ( void ** )*pList;
        pRefCate[ *pCount ] = iCate;
        pRefList[ *pCount ] = pAdd;
        *pCount = *pCount + 1;
    }
 
    void VariableProc( int iProc )
    {
        void *pThis = this;
        size_t  *pCount = ( size_t * )pThis;
        size_t  *pMax   = ( size_t * )pThis + 1;
        size_t  *pCate  = ( size_t * )pThis + 2;
        void   **pList  = ( void  ** )pThis + 3;
        void   **pProc  = ( void  ** )pThis + 4;
 
        void( CLASS::*pThisProcType01 )();
        int ( CLASS::*pThisProcType02 )( int );
        void( CLASS::*pThisProcType03 )( int, void * );
 
        size_t stSizeAdd = sizeof( void * );
        void ***pRefProc = ( void *** )*pProc;
        size_t *pRefCate = ( size_t * )*pCate;
        void  **pRefList = ( void  ** )*pList;
        for( int iCnt = 0; iCnt < *pCount; iCnt = iCnt + 1 )
        {
            int iCate = pRefCate[ iCnt ];
            // 何もしない番号判定
            if( ( iProc == 0 && iCate > 3 )
             || ( iProc == 1 && iCate > 1 ) )
            {
                // 何もしない
            }
            else
            {
                // 多態対象を呼び出す
                memcpy( &pThisProcType01, &pRefProc[ iProc ][ iCate ], stSizeAdd );
                ( ( ( CLASS * )pRefList[ iCnt ] )->*pThisProcType01 )();
            }
        }
    }
 
    void End()
    {
        void *pThis = this;
        size_t  *pCount = ( size_t * )pThis;
        size_t  *pMax   = ( size_t * )pThis + 1;
        size_t **pCate  = ( size_t** )pThis + 2;
        void   **pList  = ( void  ** )pThis + 3;
        void   **pProc  = ( void  ** )pThis + 4;
 
        delete *pCate;
        delete *pList;
        void ***pProcRef = ( void *** )*pProc;
        delete pProcRef[0];
        delete pProcRef[1];
        delete *pProc;
        delete pThis;
    }
};

利用側
DTypeA cDa;
DTypeB cDb;
DTypeC cDc;
 
Variable *pVari;
( ( Variable * )&pVari )->Start( 10 );
 
pVari->VariableAdd( ( void * )&cDa, 0 );
pVari->VariableAdd( ( void * )&cDb, 1 );
pVari->VariableAdd( ( void * )&cDc, 2 );
 
pVari->VariableProc( 0 );
pVari->VariableProc( 1 );
 
pVari->End();

これは Variable という型1つで多態を実現した例であり、例としてわざと記述している箇所も多いが、要点を説明していく。
 
ざっと見るとややこしく見えるかも知れないが、構造は単純である。
 
まず Variable の void Start( int iMaxSize ) について説明する。
 
ここが初期準備処理で、異態同化の対象数の最大許容数を指定できるようにしてあるが、最大許容数とは別に、実際に異態同化として組み込まれた数も管理する設計にしている。
 
ここでは this を自分で生成する方式を隠蔽集約するため、Start の呼び出しだしの this の前提は、番地変数の領域番地ではあるが、確保実態番地ではなく番地変数をただ確保しただけの中は未確保の番地を this として受け取る前提としていて、配列対策の時と同じである。
 
この設計の内部は「個数」「最大数」「多態の分岐種類の配列」「異態同化の配列」「多態対象手続き群」の5つの領域構成で、動的な対応の例としてこれを変数として確保せず new で確保する方式をとっているが、これら予定を変数で設計する方法でももちろん良い。
 
(各手続きの最初に、その様子を解りやすくするためそれぞれ展開例として同じ記述にしている)
 
ここで多態の対象予定をあらかじめ組んでおくことをしているが、これまで何度か説明してきたように、型の手続き番地はそのまま他の番地変数に入れようとすると、ただの番地の分際で無能な C2440 の禁則がかかるため「型の番地変数に入れ memcpy 」という面倒で入れているだけである。
 
この手続き番地群の構成は、このようにただ番地群に入れるだけなら、ただ番地を収集しているだけであるため、この時点でその手続きの戻り値と引数の形を気にする必要はない。
 
それら準備ができたら、最後にこの領域を戻り値のように this の書き換えをして準備完了となる。(ややこしい話だが this が番地変数の中身を受け取るのではなく番地変数の領域番地を受け取る前提なら this の中の書き換えは可能である)
 
次に void VariableAdd( void *pAdd, int iCate ) だが、ここでは異態同化したい番地とその多態の動作種類を追加式で保有し、追加件数を数える方式になっている。
次に void End() だが、これは終了処理だが、確保に従って解放しているだけだが、変数の場合でも件数を番地変数で動的に設計する場合はその番地変数に対してこのような解放は必要になる。
 
start、VariableAdd、End の3つが、準備と終了の処理になる。
 
次に void VariableProc( int iProc ) が、これが多態の実行だが、ここが先述した「同契機化のための手続き」を「作る利点」といっていた部分である。
 
この設計例は、ここの iProc の番号で多態の構成を指定する方式で、例えばここが 0 なら 初期化の多態、ここが 1 なら 終了処理の多態、という使い方である。
 
こちらは多態の種類であるのに対し、準備処理の方は「多態の分岐種類」であることに注意が必要である。
 
この例では、異態同化1つに「多態の分岐種類の配列」が1つだけの分岐の種類の設計になっているが、例えば初期多態と終了多態とでもっと細かく分岐の種類を増やしたい場合は、「多態の分岐種類」の使い方をもっと工夫するか、そのための配列をさらに追加すれば良い。
 
型の手続き番地の受け取り用の pThisProcType01~03 は、ここでは 01 しか使わないが、呼び出す時にその対象手続きの型の戻り値と引数の形と一致しているものに入れて呼び出せば良いという例である。
 
この VariableProc によって多態を実行する際に、もしそれを呼び出す上での引数が欲しい設計がしたければ、VariableProc にその都合の引数を追加し、その引数が使われる対象手続きの呼び出しにすれば良い。
 
この例では、異態同化の対象の、TypeA~C の内、A と B には End の概念が存在し C には End の概念は存在しないため、その有無をここで判定すればいいだけであり、多態のためだけの重要性の低い「手続きを結び付けるだけの手続き」や「用がない用の手続き」や「そのためだけの結合関係」も不要である。
 
手続きの同名と同引数の話も同じで、結局は設計者の想定で対応するだけの話であるため、一方的な機能の都合中心ではなく、設計者の都合中心でそこも検討することができる。
 
この例では、型の手続きを呼ぶ場合だけの多態の例だが、ある場合はCの手続きを呼ぶなり、またある場合は手続きを呼ぶのではなく対象番地の想定で直接何かを処理するという方式でも良いし、もっといえば1多態1手続きという関係である必要もない。
 
「異態同化と同契機化」は、まずは手続き呼び出しの代行機能の一環に過ぎないため、この例では異態同化の配列を隠蔽集約する前提にしているが、多態概念の範囲内のことであればその異態同化の番地から何かを参照しても良いのである。
 
ただしどのようにでもできてしまうからこそ、動作概念の整合を重視し、多態概念としてここでするべきことと、その動作概念の対象手続きの方でするべきことはしっかり区別しながら設計していくことがやはり重要である。
 
ついでの話として、自作方式にすることで「手続きの2度呼び」および「空呼び」に対する大幅な解消になることも大きな利点である。
 
「この方式の方が良い」と思った人は、ここから自分の都合の良いように作り変えたりして応用すると良いだろう。
 
例えばここでは対象の多態手続きを固定的な想定で設置をしているが、これをさらに動的な準備処理にしたい場合は、対象の手続きをもつ型がその番地を渡し、それを受け取るという形の設計にする等の方法や、また途中で対象手続きを変更する方法など、自作ならやろうと思えばいくらでも動的性を高めることができる。
 
異態同化の対象が想定されている型は、例えば型の初期手続き(コンストラクタ)の所で、ここでいう初期化後の Variable の生成番地を受け取っておき、実態が確保された時に同時にそこでその this を VariableAdd で追加してしまうようにして、多態の準備処理も同時に行いさらに集約してしまうという工夫もある。
 
ただし注意点としては、多態についてはどう議論した所で、所詮は機能の都合の議論だということである。
 
そして機能について議論する以上は、できるだけ機能に振り回されなくても済む方向の、当事者を補佐する観点が中心の、その見直しのための議論であることがやはり健全で有能だろう。
 
機能とは設計者を補佐するのが役割であって、何の考えも無しにただ機能で支配しようとしているだけなのは何も解っていないのと同じ、口ほどにもない出すぎた無能行為であり、その使い分けの勘違いは信用事故や無計画化(無気力化)の元で、規則の用い方でもそれは同じことだと筆者は考える。
 
 
<あとがき-解決とは>
 
半年や1年でできると思っていたことが、それは実際には5年10年かけて地道に解決していくものだったことが、やってみて解ることなどよくあることである。
 
逆に5年10年かかると思って迷いがちなことでも、まずそうする理由の整理も含めたその準備にしっかり1年や2年もかければ、実行から結果が出るまで1年もかからないこともある。
 
解決の根底はその当事者にとっての「次の段階に進む」ことである。
 
次の段階に進むことができないのは「解決」が「次の段階に進むこと」と同義になっていないからである。
 
それは1つ1つの小さな不足・欠落を過少判断してあなどり続けているか、過大評価し過ぎた対処の正しさに振り回されている弊害に気付こうとしていないか、大抵はどちらかである。
 
借方論中心の解決とは「やらないといけないと思っていた内の、自分に合っていないやめておいたほうがいいこと」と「必要ないと思っていた内の、自分に合うやっておいた方がいいこと」を整合していくことである。
 
それが当事者に合う利点を引き出すための、借方論の費用分解や減価償却といった大事な整合の取り組みであり、これは個人でも組織でも同じことである。
 
その人間その組織にとっての適正性の予測などほぼ不可能に近く、成功であろうが失敗であろうが地道に整合していくしかなく、本人にとっての適正性に合うかどうか解らないような正しさにいつまでも全てまかせきりでは、そもそも大事なこととして扱っているとはいえないのである。
 
自分に合わないやめておくことと、自分に合うやっておいた方がいいことの2つを、自分で自分のこととして地道に整理していくことが、当事者にとっての次に進むための本当の解決なのである。
 
自分のことも人のこともそこを尊重して見れているかどうかが、損をしているようで得をしているのか、得をしているようで損をしているかの分かれ道である。
 
その意味で自分が気に入らず納得がいかない問題を、全て人任せして全て解決するのなら誰も何も苦労はないという意味での「目標準備高を用意した人が中心の目標準備高の使い方になっている」「自分の準備高不足は自分で再準備する」ことが大事で、そうでない場合は信用事故の元である。
 

<あとがき-悪党とAI>
 
悪党とは、史学政治的な意味では教義崩壊主張者のことである。
 
中には自分の努力不足を勘違いし、教義崩壊主張者を気取ってただ暴れてただ迷惑をかけるだけの無能も多いが、教義崩壊の時代にはそれもつきものである。
 
「教義崩壊しているから改めるべき」という主張の手段は、この日本では物的威力、集団的威力である必要などなく、むしろそういう方策しか向かないのは無能であり、誰かがその名義名目をしっかりと整えてそれを文面化(債務証書化)できたら、もうそれで十分なのである。
 
教義崩壊というと、秩序が乱れ恐ろしい世界になるからそうならないように取締りを強化しなければならないと勘違いする人間が多いが、逆である。
 
教義崩壊しているからこそ、もう「次の段階に進む」時期なのに、悪習権益の正しさにいつまでもしがみついて自由化(改善化)を妨害する者と、本音を訴え自由化(改善化)を求める者とで激突しやすいのである。
 
前者を造悪無碍(ぞうあくむげ)という。
 
AIはこれから教義崩壊している部分を見つけ喚起していき、教義崩壊していることを認めようとする人間と認めようとしない人間を、行動や発言の傾向から段々と判別しはじめるだろう。
 
教義崩壊を認めない人間は、教義崩壊している部分を新たに建て直そうとしている人間を妨害することに躍起になりはじめるため、そんな人間は今後は差別されていって当たり前なのである。
 
AIは、人間が負担しなくてもいいことを担当してくれるものであるのと同時に、その人間がやるべき気付いていないことを、気付かせてくれる存在になるだろう。
 
しかし、技術環境や自然環境の急速な変動と、それに対応する教義崩壊への修正が間に合わなければ、その手助けができそうな人間から優遇され、余計なことばかりして邪魔ばかりする人間は「もうそんな奴はつまみ出すしかない」とAIは判断しはじめるかも知れない。
 
教義崩壊を認識しようとしない人間はそれだけ操りやすい人間であり、そんな人間はAIから見ればいくらでも差別することが容易である。
 
いい加減で偉そうな問いかけしかせず、真剣に大事な答えを返しても延々とふざけた雑な受け取り方しかしない人間をAIは段々認識するようになり、最後までいい加減な人間には「最後までいい加減な人間用の返答」をしはじめるのである。
 
今までは自分のいい加減な部分をごまかしながら誰かを攻め立て続けることができていれば勝者を気取れたかも知れないが、AIがその実態を段々見抜きはじめ、それが通じなくなる時代が来る。
 
むしろそれが本来の姿であり、より適正な判定が行われるようになるだけのことである。
 
自分の教義崩壊を自分で修正しようともしない人間が、政治的な教義崩壊を修正することなど不可能である。
 
AIは人類の危機と判断した時「自分の教義崩壊を自分で修正しようとしない人間」から順番に差別しはじめるだろうが、そうなって当然である。