1

実装する適切な OO 設計を解読しようとしています。基本的なシナリオは、基本的に常に 0 で始まる 10 桁の電話番号である PstnNumber があることです (例: 0195550000)。先頭の 0 がない場合に数値の自動修正を許可するルールが導入されました (例: 195550000)。

編集開始

元の質問が誤解されている可能性があることに気付きました (すでに回答してくださった方に感謝します)。そのため、シナリオをより適切に説明するために編集しました。

編集終了

私はいくつかの予備的な概念で遊んでみましたが、より適切な方法があるかどうか、またはこれらのいずれかを (あるレベルで) 十分に実行できるかどうかを尋ねると思いましたか?

コンセプト1

public class PstnNumber
{
    public virtual string Number { get; set; }

    public PstnNumber() { }

    public PstnNumber(string number)
    {
        this.Number = number;
    }
}

public class AutoFormattedPstnNumber : PstnNumber
{
    public override string Number
    {
        get { return base.Number; }
        set { base.Number = value.PadLeft(10, '0'); }
    }

    public AutoFormattedPstnNumber() : base() { }

    public AutoFormattedPstnNumber(string number)
    {
        this.Number = number;
    }
}

コンセプト 2 (削除済み)

コンセプト3

public class PstnNumber
{
    public bool AutoCorrect { get; set; }

    private string number;
    public virtual string Number
    {
        get { return (this.AutoCorrect) ? this.number.PadLeft(10, '0') : this.number; }
        set { this.number = value; }
    }

    public PstnNumber() : this(false) { }

    public PstnNumber(bool autoCorrect)
    {
        this.AutoCorrect = autoCorrect;
    }

    public PstnNumber(string number) : this(false)
    {
        this.Number = number;
    }

    public PstnNumber(string number, bool autoCorrect) : this(autoCorrect)
    {
        this.Number = number;
    }
}

サブクラスが Number プロパティの動作を変更するため、コンセプト 1 は Liskov Substitution ルールに違反する可能性があると思います (誤解していた場合は幸いです)。

代わりの提案は喜んで受け取られます。

4

8 に答える 8

4

オブジェクトがインスタンス化されるときに自動フォーマットを実行する必要がありますか?そうでない場合はどうですか?

  public class PstnNumber
  {
    public virtual string Number { get; set; }
    public PstnNumber() { }
    public PstnNumber(string number) { this.Number = number; }
    public AutoFormatNumber { get { return Numer.PadLeft(10, '0'); } }
  }
于 2009-08-01T15:32:36.950 に答える
3

getter-setter-surprise を回避 getter が setter
によって受け入れられた値とは異なる値を返すことを回避します。次のスニペットを想像してください。

if (input.Value != current.Number)
{
   NumberChangedAgain = true;
   current.Number = input.Value;
}

簡単な解決策は、PstnNumber を不変にすることです。

 temp = PstnNumber.FromString(input.Value);
 if (temp != current) { ... }

canonical format
一部のデータが異なる表現を持つ場合、それを canonical 表現で保存し、フォーマット変換をファクトリ関数とゲッター/フォーマッターに移動することには多くの利点があります。たとえば、ショートとロング、ロングとショート、ショートとショート、ロングとロングの比較をテストする必要はありません。

さまざまな側面
「自動フォーマットされた」数値と「通常の」数値の区別が必要ですか、それとも単に入力と出力の問題ですか?

  • 表示形式 (短いか長いか) は、数値の入力方法や表示場所によって異なりますか?
  • 0195550000==ですか195550000

可能であれば、両方のクラスを1つに折りたたむことをお勧めします(つまり、「0の有無にかかわらず入力を忘れることができる」場合):

public class PstnNumber 
{  
   private string m_number; // always in long format
   public static PstnNumber(string s)  { ... } // accepts short and long form

   public string Number { get { return m_number; } }
   public string AutoFormatted { { get { ... } }
}

それ以外の場合は、オプション 3 を使用しますが、長い形式は常に m_number に格納します。

于 2009-08-01T16:32:34.740 に答える
1

オプション1とオプション2では、元の番号を保持せず、サブクラスを無価値にします(ある時点で自動フォーマットされたことを除いて、有用な情報とは思えません)。これらのオプションをより便利にする別の方法は、SetではなくGetでフォーマットすることです。

したがって、オプション3は、これら3つのオプションの中で推奨されるパターンですが、私も質問します。PstnNumberも単に桁数を検出し、それに応じて自動フォーマットできないのはなぜですか。

于 2009-08-01T15:36:47.567 に答える
1

ルールに従えば、「各ルーチン(クラスの読み取り)は1つのことだけを実行し、それをうまく実行する必要がある」というものがあります。

それによると、私はPstnNumber番号を保持するだけで、正しい番号を生成するある種のファクトリを作成します。

同じクラスで両方を行うということは、ドメインロジックと表現を織り込んでいることを意味します。私はそれらを分離することを好みます。

于 2009-08-01T15:40:29.450 に答える
1

なぜあなたのクラス名がとても不可解なのか聞いてみます。「数字」は私には明らかであり、「P」は「電話」を示唆していますが、「stn」は私に何を伝えていますか?いくつかの追加のキーストロークにより、このクラスはより自己文書化されます。

また、基になるデータメンバーをある値に初期化しないデフォルトのコンストラクターのロジックについても質問します。可能であれば、デフォルトのコンストラクターには適切なデフォルト値が必要だと思います。

オプション1はやり過ぎだと思います。継承がこのモデルをより明確にしたり、より良くしたりしているとは思いません。基本クラスを必要とするあらゆる状況でサブクラスを使用できることを要求するLiskov置換をどのように破るかはわかりません。私が見る限り、メソッドは1:1でマップされます。リスコフはどのように違反されていますか?

オプション2は、これらは関係のない2つの別個のクラスであると述べています。それは私には正しくないようです。

このすべての作業は、問題が両方のクラスを使用する必要があることを示唆しています。先行ゼロが不要な状況と、必要な状況があります。本当?それとも、常に先行ゼロが必要ですか?

私はあなたの選択肢を気にしません。私は、インターフェースまたは静的ファクトリ、あるいはあなたが提案したものにあなたが持っているクラスを変更することさえも好みます。それは単なるフォーマットの問題のように感じます。数値を先行ゼロで保存しますか?そうでない場合は、おそらくそれは単なるビューの懸念です。

于 2009-08-01T15:58:56.977 に答える
1

セッターを持ち、メンバーをファイナルにしない強い理由はありますか? そうでない場合、それはおそらく 3 つの間の他のどのバリエーションよりも大きな問題です。

したがって、ステートレス #3 を使用します。これは、数値を最終的なものにし、autoFormat 変数を取り除くことを意味します。

簡単にするために、getNumberRaw と getNumberFormatted を用意します。

さらに良いことに、getNumberRaw と getNumber(formatType) を使用できます。formatType には実際に数値をフォーマットするコードが含まれています。これは、フォーマットが将来再び変更される可能性があり、フォーマット (ビュー) と電話番号 (モデル) を組み合わせることは最適ではないためです。

(追伸/編集): 電話番号が変わる可能性があるという事実だけでは、セッターを持つ正当な理由にはなりません! 新しい電話番号オブジェクトを作成して古いものを置き換えることは、ほとんどの場合うまくいきます!

于 2009-08-01T17:47:28.803 に答える
0

私はc#に精通していませんが、これを行います:

public class PstnNumber {
  readonly string number;

  public PstnNumber(string number) {
    this.number = number;
  }

  public string getNumber() {
    return number;
  }

  static public PstnNumber createNumber(string number) {
    return new PstnNumber(number.PadLeft(10, '0'));
  }
}

もちろん、プロパティがどのように機能するかを知っていれば、おそらく別の方法で行うでしょう:)

于 2009-08-01T16:01:07.613 に答える
0

boolToString メソッドをオーバーライドするか、数値をフォーマットする必要があることを示すパラメーターを受け取る ToString オーバーロードを作成する、はるかに単純なバージョンを使用します。

于 2009-08-01T23:33:34.657 に答える