6

これについて非常に多くの記事を読みましたが、まだ 2 つの質問があります。

質問 #1 -依存関係の逆転について:

高レベルのクラスは低レベルのクラスに依存すべきではないと述べています。どちらも抽象化に依存する必要があります。抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。

例えば ​​:

public class BirthdayCalculator
{
    private readonly List<Birthday> _birthdays;

    public BirthdayCalculator()
    {
        _birthdays = new List<Birthday>();// <----- here is a dependency
    }
...

修正:それをctorに入れてください。

public class BirthdayCalculator
{
    private readonly IList<Birthday> _birthdays;

    public BirthdayCalculator(IList<Birthday> birthdays) 
    {
        _birthdays = birthdays;
    }
  • それがctorにある場合-クラスを使用するたびに送信する必要があります。BirthdayCalculatorそのため、クラスを呼び出すときにそれを保持する必要があります。そんなことしていいの?

  • 私は、修正後も -IList<Birthday> _birthdaysそこ (BirthdayインIList) ではなく - であるべきだと主張することができますIList<IBirthday>。私は正しいですか?

質問 2 - Liskov 置換について:

派生クラスは、それらの基本クラスに置き換え可能でなければなりません

またはより正確:

q(x) を、型 T のオブジェクト x について証明可能なプロパティとします。その場合、q(y) は、S が T のサブタイプである型 S のオブジェクト y に対して真でなければなりません。

(すでにこれを読んでいます)

例 :

public abstract class Account
{
     public abstract void Deposit(double amount);
}

私はクラスを持っています:

public class CheckingAccount : Account
{
     public override void Deposit(double amount)
    {    ...
        _currentBalance += amount;
    }
}

そして銀行は住宅ローン口座を開設したいと考えています - だから:

public class MortgageAccount : Account
{

    public override void Deposit(double amount)
    {
        _currentBalance -= amount; //<-----notice the minus
    }
}

問題は、住宅ローンを受け取り、 Account預金を行う機能がある場合に発生します。

public class Bank
{
    public void ReceiveMoney(Account account, double amount)
    {
        double oldBalance = account.CurrentBalance;
        account.Deposit(amount); //oopssss?????
    }
}

ここでは、LSP に違反しています。

しかし、 私は理解していません。

オーバーライドされたすべてのメソッドは、オーバーライドされたときに異なるコードを実行するため、100%置き換え可能になることはありません!

定義では、「ロジックは基本クラスと同様に継続する必要があります(常に正の数を預ける(追加する))」については言及されていません。

例:

CheckingAccount class とclassの両方MortgageAccount が正の数をデポジットしていMortgageAccount て、 db にもログを記録していたとしたら? それはまだ LSP を壊しますか? ブレーク/非ブレーキ LSP の境界は何ですか?

定義は、その境界が何であるかを定義する必要があります。そして、それについて何も言っていません。

私は何が欠けていますか?

4

6 に答える 6

5

LSP は、基本クラスが行う約束は、サブクラスも行う必要があると述べています。口座の場合、はい、つまり、住宅ローンの残高からDeposit 差し引くのはかなり面倒です。これに対する 1 つの回避策は、残高を、顧客があなたに負っている金額とあなたが彼に負っている金額との差額であると考える場合です。(とにかく、それが「残高」の元の意味であると約 54% 確信しています。) プラスの残高は、顧客がお金を持っていることを意味する場合があり、マイナスの場合は、顧客がお金を借りていることを意味する場合があります。2 つのアカウントを同様に処理できるように解決できない場合は、それらを関連付けるべきではありません。少なくとも、基本クラスで「Deposit」メソッドを定義するべきではありません。

DIP に関する限り、それが実際に言及していないのは、コードのすべての行に適用することを意図していないということです。最終的には、どこかに具体的なクラスが必要です。ポイントは、これらの具体的なクラスの詳細への依存を、それらについて絶対に知る必要があるコードのセクションに制限し、それらのセクションの数とサイズをできるだけ小さく保つことです。つまり、できるだけ一般的なインターフェイスを使用することを意味します。たとえば、すべての保証List(列挙の順序、重複の存在、null など) が必要ない場合は、次のように宣言できます_birthdaysIEnumerable代わりは。コンストラクターが IEnumerable が実際にリストであることを知っている唯一のものである場合、多かれ少なかれ原則に従っています。

Object(ただし、注意してください: これは、すべてを として宣言し、必要に応じてダウンキャストするだけで DIP に準拠できるという意味ではありません。より具体的なものを取得しようとしています。)

于 2012-12-17T18:00:23.680 に答える
2

それがctorにある場合-クラスを使用するたびに送信する必要があります。したがって、BirthdayCalculator クラスを呼び出すときにそれを保持する必要があります。そんなことしていいの?

誕生日のリストを1回取得し、1つ作成BirthdayCalculatorして、それを渡します。BirthdayCalculator誕生日のリストが変更された場合、または何らかの形で状態を維持する場合にのみ、 new をインスタンス化する必要があります。

修正後も、IList<Birthday>_birthdays は存在しないはずです (IList の誕生日) IList<IBirthday>。私は正しいですか?

これは依存します。誕生日は単なるデータ オブジェクトですか? その中に含まれているロジックはありますか?それが単なるプロパティ/フィールドの集まりである場合、インターフェースはやり過ぎです。

誕生日にロジックがある場合は、 を使用しIEnumerable<IBirthday>ます。 List<Birthday>は に割り当てられないためIList<IBirthday>、そこで問題が発生する可能性があります。操作する必要がない場合は、そのままにしてくださいIEnumerable


2 番目の質問に関して、私が問題を抱えているのは命名規則だけです。あなたは住宅ローンに預金をしません。そのメソッドの名前を変更すると、より理にかなっている可能性があると思います。

また、当座預金口座と住宅ローン口座は 2 つの非常に異なる動物です。1 つは預金口座で、もう 1 つはローンです。私はそれらを同じ意味で使用しようとすることに非常に注意を払います.

編集

他の人は、負の値を作成_currentBalanceしてから追加できることに注意しました。MortageAccountその方が理解しやすいなら、それを選んでください。ただし、チェック/表示を使用する方法が何であれ、それを適切に処理するMortageAccount限り、そのバランスを処理する方法の内部は重要ではありません。GetBalance

于 2012-12-17T17:54:06.160 に答える
1

#1

  • はい、オブジェクトを作成するたびにそれを呼び出す必要があります。これは、ファクトリ制御の反転フレームワークが便利な場所であり、実行しなければならないすべての配管を削除します。

  • はい、IBirthday インターフェイスを使用する必要があります。

#2

ここであなたの論理に欠陥があります。口座にお金を入金すると、お金が追加されます。住宅ローン口座には口座にお金がなく (プラスの残高)、預金によって口座からお金が取り除かれていません。住宅ローン口座の残高がマイナスで、口座に入金すると残高が減少します (マイナスの残高に追加されます)。

于 2012-12-17T18:01:25.103 に答える
0

依存性逆転:

コンストラクターに含まれる場合は、クラスを使用するたびに送信する必要があります。BirthdayCalculatorですから、クラスを呼び出すときはそれを保持する必要があります。そのようにしても大丈夫ですか?

オブジェクトを使用するときではなく、オブジェクトを作成するときに必要BirthdayCalendarです。を構築インターフェースに移動すると、のすべての依存関係が呼び出し元の依存関係と同じになる(つまり、新しい依存関係がない)IList<Birthday>ことが保証されます。これが、私たちが達成しようとしていることです。BirthdayCalendar

私は、修正後も、それでも-IList<Birthday> _birthdaysそこまでではないはずです(Birthdayin IList)-と主張することができますが、それはのようにする必要がありますIList<IBirthday>。私は正しいですか?

これは、依存性逆転の原則とは直接関係ありません。依存関係を呼び出し元にオフロードし、この依存関係の詳細について質問しています。IBirthdayの代わりにリストの内部にアクセスするために使用するのは良い考えかもしれませんがBirthday、依存関係の逆転とは関係のない、インターフェースの問題です。

オーバーライドされたメソッドはすべて、オーバーライドされたときに異なるコードを実行するため、100%置き換えられることはありません。定義では、「ロジックは基本クラスのように継続する必要があります(常に正の数をデポジット(追加)する)」については説明していませんでした。

例:CheckingAccountクラスとMortgageAccountクラスの両方が正の数をデポジットしているだけでなくMortgageAccount、dbにログを記録している場合はどうなりますか?それはまだ壊れていLSPますか?ブレーキなし/ブレーキなしLSPの境界は何ですか?定義は、その境界が何であるかを定義する必要があります。そしてそれについては何も言っていません。

リスコフの置換原則は、基本クラスと派生クラスの関係に適用できます。あなたの例は共有ベースから継承する2つのクラスを扱っているので、あなたの関係は派生クラス1と派生クラス2です。これは、LSPが話していることとは正反対です。これは、まさにあなたが述べた理由によるものです。ロジックは、両方の派生クラスで異なります。LSPは、定義した継承の有効性の尺度です。質問に前向きに答えることができれば、私の派生クラスオブジェクトは、基本クラスオブジェクトが使用されているすべての場所で透過的に使用できますか?、LSPを壊していません。そうでなければ、あなたはそれを壊しています。

于 2012-12-17T18:08:12.410 に答える
0

最初の点に関しては、特定の低レベルのクラスを他のクラスのプライベート実装の詳細として使用するのは問題ありません。SOLIDと言えばコードはもっと多いかもしれませんIList<Birthday> _birthdays = new List<Birthday>;が、パフォーマンスはおそらくわずかに良くなるでしょうList<Birthday> _birthdays = new List<Birthday>IBirthdayではなくを使用したい場合は、コンストラクターまたは静的メソッドを呼び出す場合を除いBirthdayて、IBirthday毎回使用する傾向がある場合はそれでも構いません。vs 、およびvsBirthdayを使用するかどうかについて一貫性を保つ必要があることに注意してください。BirthdayIBirthdayList<T>IList<T>

プライベートメンバーは、パブリックメンバーと同じルールに従う必要はないことに注意してください。型が型のパラメータを受け入れる場合、List<T>外部コードはその型を使用するように強制されます。List<T>からに切り替えるにIList<T>は、多くの異なるアセンブリで多くの変更を行う必要があり、古いバージョンのアセンブリを新しいものと互換性のないものにする可能性があります。対照的に、クラスにList<T>外部コードと共有されることのないタイプのプライベートメンバーがある場合、使用IList<T>するように変更した場合の影響はクラス自体の内部に限定され、そのクラスのコンシューマーには影響しません。

于 2012-12-17T18:09:23.713 に答える
0

LSPについて:私が見ているように、LSPは主に階層の(公開)契約に関するものです。あなたは口座(オブジェクト)を持っています、あなたはそれにお金を貸方/借方に記入することができます(契約)。住宅ローン口座の場合、貸方は減算になり、借方は加算になります。当座預金口座の場合はその逆です。

重要なことは、アカウントが与えられた場合、いつでもクレジット/デビットアクションを実行するように依頼できることと、アカウントオブジェクトのすべてのサブクラスがそれらの操作をサポートすることです。

于 2012-12-17T18:02:14.590 に答える