🗿

オブジェクト指向に対する批判記事を読んで、サンプルコードも交えて現時点の加賀美なりの考えを書いてみる。。。Orz

10 min read

<障害発生日>
2021年9月26日頃...かな(;^_^A

<ざっくりとした環境情報>
OS:Windows10
言語:Java (jbr-11(JetBrains Runtime version 11.0.10))

Twitter上でオブジェクト指向の問題点を指摘するツィートを発見!!その後自分でもネットで調べたりしてとても納得する。。。

みなさま、おはようございます✨
さて、タイトルにもございますように一昨日あたりでしょうか?
ネット検索をして下記のような記事を発見いたしました。。。

https://okuranagaimo.blogspot.com/2019/07/1.html

<※以下一部引用>

オブジェクト(または名詞)は、OOPの中核を成すものです。OOPの根本的な制限は、それがすべてを名詞にすることです。そして、すべてが名詞としてモデル化されるべきではありません。操作(機能)をオブジェクトとしてモデル化しないで下さい。2つの数を乗算する関数だけが必要なときに、なぜMultiplierクラスを作成しなければならないのでしょうか? 単に乗算機能を持ち、データをデータにし、機能を機能にしましょう。

そーいえば以前カガミもツィートで、『オブジェクト指向の『オブジェクト』とは、『名付けえるモノ』だ』とかなんとかツィートしましたよね(^^
※ってツィートしたハズなのにそのツィートなぜだか見つからねぇ。。。Orz

で、ですね。

『読むだけでわかる!?オブジェクト指向デザインパターンの基礎の基礎』

などというシリーズ本を出している加賀美としては、なんとかして反論したいような気持にもなりましたが、正直なところここに書いてある批判の多くは素直に認めるべきかなと(^^;

で、その後ネットサーフィンしながらOOP関連、あるいは『ソフトウェア工学』関連のキーワードでいろいろと読み漁っていたら、下記のような記事も見つけました。

https://ubiteku.oinker.me/2017/02/01/elm-and-kubernetes/

<※以下一部引用>

関数型の回りくどさに加えて、Elm には厳密な型システムがある。実は今、開発スピードを減退させている最大の原因はこの型システムである。代数的データ型とか、今まで遭遇したことのないコンセプトに純粋な感動を覚える一方、曖昧さを許さない型システムのおかげで、型を合わせるためにどうすれば良いかというのを考えてるだけで膨大な時間が過ぎて行く。

あら?
先の引用『オブジェクト指向プログラミング -- 1兆ドル規模の大失敗』では、記事の最後の方に『代替案は関数型プログラミングです♪♪♪』と思えるような記述があったのですけど、ぱっと見た限りでは関数型プログラミングでも詰まる方は詰まるようですね。。

※再掲:

https://okuranagaimo.blogspot.com/2019/07/1.html
<以下一部引用>

代替案は何ですか?
ネタバレ警報: 関数型プログラミング

これら、現れては消え、現実にWebアプリケーションなどを作ってく我々の行く手を阻むものはいったいなんなのでしょうか。。。

現実には、我々プログラム開発者は永遠に『泥臭い泥沼の中』をはい回らねばならないのでしょうか。。。。

カガミなりの答え:OOP、FPなど、『ソフトウェア工学』的な資産はそのままでは現実のアプリケーション開発には有効とはならないのではないか

これもうろ覚えなのですが、作家の阿刀田高さんは、ギリシャ神話やキリスト教、はてはイスラム教についてまでわかりやすく、時にエロトークを交えて書かれておられるのですけど、そのことについてその道の専門家の学者さんが『上手な文章でうらやましい』みたいなことを述べられておりました。

あ、これも検索してもなかなか出ないので細かいニュアンス間違ってるかもしれません(^^;;

https://www.amazon.co.jp/dp/B00DOT4YCY/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

で、作家さんにとって、化学や物理学、あるいは宗教学って、『より面白い本を書くためのネタ』に過ぎないと思うのですよね。。。

https://www.amgakuin.co.jp/contents/novels/column/be-novelist/novelist-ability/

で、ですね。

最終的なあっしの所感としては、画面やバッチ、あるいは非同期メッセージなどを伴ういわゆる『エンプラアプリケーションの開発』って、様々な知識を基に、壮大なエンターテインメントを提供する小説家の『文筆業』に近いような気がするのです。。。

OOPで大御所のマーチン・ファウラーさんも、名著『エンタープライズアプリケーションアーキテクチャパターン』の中でこのように述べられておりますよね。。。

多くの人がコンピュータソフトウェアを作成し、私たちはそれらをすべてソフトウェア開発と呼んでいる。しかし、ソフトウェアにもいろいろなものがあり、それぞれ目的も違えば複雑さの度合いも違う。電気通信業界の友人と話をしたときに気づいたのは、エンタープライズアプリケーションは電気通信ソフトウェアよりもある意味で扱いやすいということだ。厄介なマルチスレッドの問題は存在せず、ハードウェアとソフトウェアの統合も不要である。しかしとても難しい面もある。エンタープライズアプリケーションでは、複雑なデータに対処しなければならない場合が多い。しかも論理的とはいいがたいビジネスルールに従いながらである。

マーチン・ファウラー.エンタープライズアプリケーションアーキテクチャパターン(p.2).翔泳社.Kindle版.
(※太字はカガミによるもの)

つまり、ソフトウェア工学だと、『工学』だから『学問』とならざるを得ない。
そして、『工学』なので、『科学』である。
なので、オブジェクト指向であるとか、関数型であるとか、そういった類の『原理』や『原則』を求めざるを得ないのかなと思うのです。

しかし、我々のような、実際に現実のエンプラアプリケーションの泥沼の中をがっぷり四つ巴(よつどもえ)で悪戦苦闘している身としては、文字通り『ソフトウェア工学は(エンプラアプリケーションの)銀の弾丸とはなりえない』と感ぜざるを得ないのです。。。

前置き長っ(;´Д`) それではちょこっとサンプルコードを。

ではそれではやっとか💧
批判が多々きそうですけど、昨日思い付きで書きなぐった下記のサンプルコードをご覧ください。

前段:OOのポリモーフィズムは親子関係なので『区分け』について厳密過ぎる。ので、従来では不要だった『プログラムの書き直し』が多発する(被害者談)。

※元ネタ:

http://www.oocities.org/tablizer/oopbad.htm
<以下一部引用>

IF statements, case-blocks, and data query expressions are usually more flexible in dealing with changing dispatching (rule triggering) criteria; even multiple aspects can participate in an IF expression. It is very tricky and messy to achieve the same with polymorphism. Changing the IF criteria does not require moving the IF block.
Polymorphism is just too narrow a concept to be flexible. It generally requires a single, clean, global, and stable taxonomy/division-criteria to be effective, and those are hard to find except perhaps in nature (geometry, physics, etc.).

IFステートメント、ケースブロック、およびデータクエリ式は、通常、ディスパッチ(ルールトリガー)基準の変更をより柔軟に処理できます。複数の側面でさえIF式に参加できます。多形性で同じことを達成することは非常にトリッキーで厄介です。 IF基準を変更する場合、IFブロックを移動する必要はありません。
多形性は、概念が狭すぎて柔軟ではありません。それは一般に、効果的であるために単一の、クリーンで、グローバルで、安定した分類/分割基準を必要とします、そしてそれらはおそらく自然(幾何学、物理学など)を除いて見つけるのが難しいです。

カガミなりの(苦しい💧)答え:それでも、エンプラアプリケーションの煩雑なIF文地獄は苦しい。ので、OOPの原理・原則からすれば邪道だが、『ファサード』パターンで逃げられないか。

<※ポリモーフィズムの場合>

class OujiSama{

    private String name   = "名無し";
    private int    weight =  -1;
    private int    height =  -1;
    private int    age    =  -1;

    public OujiSama(String name,int weight,int height,int age){
        this.name = name;
        this.weight = weight;
        this.height = height;
        this.age = age;
    }
    
    public String name(){
        return name;
    }

    public int height(){
        return height;
    }

    public int weight(){
        return weight;
    }

    public int age(){
        return age;
    }
    
}

//比較インターフェース
interface ComparatorStrategy{
    public String compare(OujiSama o1 , OujiSama o2);
}

//体重比較クラス
class WeightComparator implements ComparatorStrategy{
    public String compare(OujiSama o1 , OujiSama o2){
        //痩せてる人が好き♪
        if(o1.weight() < o2.weight()){
            return o1.name();
        }else if(o1.weight() == o2.weight()){
            return "両方とも";
        }else{
            return o2.name();
        }
    }
}

//身長比較クラス
class HeightComparator implements ComparatorStrategy{
    public String compare(OujiSama o1,OujiSama o2){
        //背が高い人が好き♪
        if(o1.height() > o2.height()){
            return o1.name();
        }else if(o1.height() == o2.height()){
            return "両方とも";
        }else{
            return o2.name();
        }
    }
}

class Happy{
    public static void main(String args[]) {
        OujiSama Ouji1 = new OujiSama("じぃじ",68,180,70);
        OujiSama Ouji2 = new OujiSama("ぱぁぱ",71,172,41);
        OujiSama Ouji3 = new OujiSama("チェホンマン",160,218,40);

        ComparatorStrategy WeightOtokoChk = new WeightComparator();
        ComparatorStrategy HeightOtokoChk = new HeightComparator();

        String type = args[0];
        String result = "";
        //If文スッキリ(⋈◍>◡<◍)。✧♡
        if(type.equals("痩せてる")){ result = WeightOtokoChk.compare(Ouji1, Ouji2); }
        if(type.equals("背が高い")){ result = HeightOtokoChk.compare(Ouji1, Ouji3); }

        System.out.println(result + "大好き♪");

    }
}
.\03_Compare_Strategy>Java Happy "痩せてる"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
じぃじ大好き♪

.\03_Compare_Strategy>Java Happy "背が高い"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
チェホンマン大好き♪

.\03_Compare_Strategy>Java Happy "背が高い"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
チェホンマン大好き♪

.\03_Compare_Strategy>Java Happy "痩せてる"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
じぃじ大好き♪

.\03_Compare_Strategy>

<※ファサードの場合>

class OujiSama{

    private String name   = "名無し";
    private int    weight =  -1;
    private int    height =  -1;
    private int    age    =  -1;

    public OujiSama(String name,int weight,int height,int age){
        this.name = name;
        this.weight = weight;
        this.height = height;
        this.age = age;
    }
    
    public String name(){
        return name;
    }

    public int height(){
        return height;
    }

    public int weight(){
        return weight;
    }

    public int age(){
        return age;
    }
    
}

//比較サービス
class ComparatorService { 

    public String weight(OujiSama o1,OujiSama o2){ 
        return new WeightComparator().doubleCompare(o1,o2);
    }    
    public String height(OujiSama o1,OujiSama o2,OujiSama o3){
        return new HeightComparator().tripleCompare(o1,o2,o3); 
    }
}
//体重比較クラス
class WeightComparator {
    public String doubleCompare(OujiSama o1 , OujiSama o2){
        //痩せてる人が好き♪
        if(o1.weight() < o2.weight()){
            return o1.name();
        }else if(o1.weight() == o2.weight()){
            return "両方とも";
        }else{
            return o2.name();
        }
    }
}

//身長比較クラス ※ここがOO的には変化球(;^ω^)※
class HeightComparator {
    public String tripleCompare(OujiSama o1,OujiSama o2,OujiSama o3){
        //背が高い人が好き♪
        return compareRecursion(compareRecursion(o1,o2),o3).name();
    }
    private OujiSama compareRecursion(OujiSama o1,OujiSama o2){
        //背が高い人が好き♪
        if(o1.height() > o2.height()){
            return o1;
        }else if(o1.height() == o2.height()){
            return null;
        }else{
            return o2;
        }
    }
}

class Happy{
    public static void main(String args[]) {        
        OujiSama Ouji1 = new OujiSama("じぃじ",68,180,70);
        OujiSama Ouji2 = new OujiSama("ぱぁぱ",71,172,41);
        OujiSama Ouji3 = new OujiSama("チェホンマン",160,218,40);

        ComparatorService OtokoChk = new ComparatorService();

        String type = args[0];
        String result = "";
        //If文スッキリ(⋈◍>◡<◍)。✧♡
        if(type.equals("痩せてる")){ result = OtokoChk.weight(Ouji1, Ouji2); }
        if(type.equals("背が高い")){ result = OtokoChk.height(Ouji1, Ouji2, Ouji3); }

        System.out.println(result + "大好き♪");

    }
}
.\03_Compare_Service>Java Happy "痩せてる"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
じぃじ大好き♪

.\03_Compare_Service>Java Happy "背が高い"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
チェホンマン大好き♪

.\03_Compare_Service>Java Happy "背が高い"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
チェホンマン大好き♪

.\03_Compare_Service>Java Happy "痩せてる"
Picked up JAVA_TOOL_OPTIONS: "-Dfile.encoding=UTF-8"
じぃじ大好き♪

.\03_Compare_Service>

う~ん
改めて見直すとなんかイケてないな(^^;;

けど
まぁつまるところ何が言いたいのかと言いますと、
現場の人が学者にいちゃもんつけるならまだしも、エンプラの泥沼の中で共に苦闘している身内同士(=先述の海外の方々)が争ってもしょうがなくない?

ということです(^^;;

古いですけど『踊る大捜査線』思い出しますね。。。

結びに

今回、カガミが『デザインパターン』の『ファサード』でIF文地獄を抜け出すにはどうしたら良いかを提案したように、やっぱりどえらい方々、やんごとなき方々が提案する『<原理/原則>、あるいは<仕組み>』は、使い方次第ではエンプラ現場でも活かせる方法があるのではないか、とカガミは考えております。。。

けれど、エンプラ現場は『複雑怪奇なヴァリエーションを持つデータを、論理的とは言い難いビジネスルールに従いながら処理する世界』です。

なので、そのことにこだわり過ぎるのは危険ではないかとカガミは思います。

というより、このエンプラ現場の世界で何かコードを美しくする唯一の『工学的な意味合いでの原理・原則』を見つけるなんて、そもそも初めから無理なんじゃないでしょうか。

これは、現代科学が初期の近代科学が目指していた人間のWhy(この世界に対する『なぜ』)に応えるられるような『人間にとって腑に落ちる法則を見出す』のは諦め、How(この世界でより良く生きてくには『どのように』したら良いか)への法則を無理くり(に数学が苦手なカガミには見える💧)見出す学問へと変化してきたこととも似ているのかもしれません。。。

と、とりあえずお仕事始まってしまうので
今日はここまでどぇ~す✨

それではまた~✨✨✨

Discussion

ログインするとコメントできます