静的タイプチェックを使用してビジネスエラーから保護する


13

私は静的型チェックの大ファンです。これは、次のような愚かな間違いを犯さないようにします。

// java code
Adult a = new Adult();
a.setAge("Roger"); //static type checker would complain
a.setName(42); //and here too

しかし、それはあなたがこのような愚かな間違いを犯すことを妨げません:

Adult a = new Adult();
// obviously you've mixed up these fields, but type checker won't complain
a.setAge(150); // nobody's ever lived this old
a.setWeight(42); // a 42lb adult would have serious health issues

問題は、明らかに異なる種類の情報を表すために同じタイプを使用している場合に発生します。これに対する適切な解決策はInteger、ビジネスロジックエラーを防ぐためだけにクラスを拡張し、機能を追加しないことだと考えていました。例えば:

class Age extends Integer{};
class Pounds extends Integer{};

class Adult{
    ...
    public void setAge(Age age){..}
    public void setWeight(Pounds pounds){...}
}

Adult a = new Adult();
a.setAge(new Age(42));
a.setWeight(new Pounds(150));

これは良い習慣と考えられていますか?または、そのような制限のある設計で、将来予測できないエンジニアリングの問題がありますか?


5
ではないだろうa.SetAge( new Age(150) )、まだコンパイル?
ジョン・ウー

1
Javaのint型には固定範囲があります。明らかに、カスタム範囲の整数、たとえばInteger <18、110>が必要です。一部の(メインストリームではない)言語が提供する絞り込みタイプまたは依存タイプを探しています。
セオドロスチャツィジアンナキス

3
あなたが話しているのは、デザインを行う素晴らしい方法です!悲しいことに、Javaには非常に原始的な型システムがあるため、正しく実行するのは困難です。そして、それを行おうとしても、パフォーマンスの低下や開発者の生産性の低下などの副作用が生じる可能性があります。
陶酔

@JohnWuはい、あなたの例はまだコンパイルされますが、それはそのロジックの単一障害点です。new Age(...)オブジェクトを宣言するWeightと、他の場所にある型の変数に誤って割り当てることはできません。ミスが発生する可能性のある場所の数を減らします。
Jボブ

回答:


12

基本的にユニットシステムを要求しています(ユニットテストではなく、「物理ユニット」のような「ユニット」、メートル、ボルトなど)。

コードでAgeは時間をPounds表し、質量を表します。これは、単位変換、基本単位、精度などにつながります。


このようなことをJavaに取り入れる試みがありました。

後者の2つは、このgithubのものに住んでいるようです:https : //github.com/unitsofmeasurement


C ++にはBoost経由のユニットがあります


LabViewには多数のユニットが付属しています。


他の言語での他の例があります。(編集は大歓迎です)


これは良い習慣と考えられていますか?

上記でわかるように、単位を使用して値を処理するために言語が使用される可能性が高いほど、単位をよりネイティブにサポートします。LabViewは、測定デバイスとのやり取りによく使用されます。そのため、言語にそのような機能を持たせることは理にかなっており、それを使用することは確かに良い習慣と考えられます。

しかし、そのような厳密さの需要が低い汎用高水準言語では、おそらく予期しないことです。

または、そのような制限のある設計で、将来予測できないエンジニアリングの問題がありますか?

私の推測は次のとおりです。パフォーマンス/メモリ。多くの値を扱う場合、値ごとのオブジェクトのオーバーヘッドが問題になる可能性があります。しかし、いつものように、 早すぎる最適化はすべての悪の根源です

ユニットは通常暗黙的に次のように定義されているため、より大きな「問題」が人々を慣れさせていると思います。

class Adult
{
    ...
    public void setAge(int ageInYears){..}

intユニットシステムに慣れていないときに、単純に説明できるようなオブジェクトを値として渡す必要がある場合、人々は混乱します。


「人々は、単純に説明できるようなものの値としてオブジェクトを渡さなければならない場合、混乱しintます...」---このためのガイダンスとして、Least Surpriseの校長がいます。良いキャッチ。
グレッグブルクハート

1
私は、カスタムの移動にライブラリと依存型への単位の領域のうち、この範囲だと思う
JKを。

6

ヌルの答えとは対照的に、「単位」のタイプを定義することは、整数では測定値を説明するには不十分な場合に有益です。たとえば、重量は同じ測定システム内の複数のユニットで測定されることがよくあります。「ポンド」と「オンス」または「キログラム」と「グラム」を考えてください。

ユニットのタイプを定義するより詳細なレベルの測定が必要な場合は有益です。

public struct Weight {
    private int pounds;
    private int ounces;

    public Weight(int pounds, int ounces) {
        // Value range checks go here
        // Throw exception if ounces is greater than 16?
    }

    // Getters go here
}

「年齢」などの場合、実行時に人の生年月日に基づいて計算することをお勧めします。

public class Adult {
    private Date birthDate;

    public Interval getCurrentAge() {
        return calculateAge(Date.now());
    }

    public Interval calculateAge(Date date) {
        // Return interval between birthDate and date
    }
}

2

あなたが探していると思われるものは タグ付きタイプます。「これは年齢を表す整数です」と「これは整数ですが、重みを表します」と「一方を他方に割り当てることはできません」と言う方法です。これはメートルやキログラムなどの物理的な単位よりもさらに進んでいることに注意してください:私のプログラムでは、「人の身長」と「地図上のポイント間の距離」があります。どちらもメートルで測定されますが、もう一方は、ビジネスロジックの観点からは意味がありません。

Scalaなどの一部の言語は、タグ付きタイプを非常に簡単にサポートしています(上記のリンクを参照)。その他では、独自のラッパークラスを作成できますが、これはあまり便利ではありません。

検証、たとえば、人の身長が「合理的」であることを確認することも別の問題です。このようなコードは、Adultクラス(コンストラクターまたはセッター)内、またはタグ付きの型/ラッパークラス内に配置できます。ある意味では、URLまたはUUID(例えばユーティリティメソッドを提供し、他の人の間で)そのような役割を果たします。

タグ付きタイプを使用するか、ラッパークラスを使用するかは、実際にコードを改善するのに役立つかどうかは、いくつかの要因に依存します。オブジェクトが単純でフィールドが少ない場合、それらを間違って割り当てるリスクは低く、タグ付きの型を使用するために必要な追加のコードは努力する価値がないかもしれません。複雑な構造と多くのフィールドを持つ複雑なシステムでは(特にそれらの多くが同じプリミティブ型を共有する場合)、実際に役立つ場合があります。

私が書いたコードでは、マップを渡すとラッパークラスを作成することがよくあります。などの型Map<String, String>はそれ自体非常に不透明であるため、などの意味のある名前を持つクラスでラップすると非常にNameToAddress役立ちます。もちろん、タグ付きタイプを使用すると、Map<Name, Address>マップ全体のラッパーを記述できますが、ラッパーは必要ありません。

ただし、文字列や整数などの単純な型の場合、(Javaの)ラッパークラスは非常に厄介であることがわかりました。通常のビジネスロジックはそれほど悪くはありませんでしたが、これらの型をJSONにシリアル化したり、DBオブジェクトにマッピングしたりするなど、いくつかの問題が発生しました。ただし、このコードに関連する余分な作業とメンテナンスは、これらのラッパーを使用することで得られるものを相殺します。もちろん、YMMVと別のシステムでは、バランスが異なる場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.