5

まず、これは長い質問のように思えるかもしれません。そうではないと思います...コードは、私が現在行っていることの概要にすぎません。それは正しくないので、建設的な批判と落とし穴に対する警告、および私にできることの提案を探しています。

ビジネス オブジェクトを含むデータベースがあります。
親オブジェクトのプロパティにアクセスする必要があります。
ビジネス オブジェクトを通じて何らかの状態を維持する必要があります。

クラスを見ると、アクセス修飾子が正しいとは思えません。あまりうまく構成されていないと思います。リレーションシップのほとんどは、パブリック プロパティでモデル化されています。SubAccount.Account.User.ID <-- それらはすべて公開されています..

これよりもクラス間の関係をモデル化するためのより良い方法があるので、それほど「公開」されませんか?

この質問の他の部分は、リソースに関するものです。

リストを返す User.GetUserList() 関数を作成し、9000 人のユーザーがいる場合、GetUsers メソッドを呼び出すと、9000 個の User オブジェクトが作成され、その中に 9000 個の新しい AccountCollection オブジェクトが作成されます。このプロジェクトがそれほどリソースを消費しないようにするにはどうすればよいですか?

以下のコードを見つけて、細断してください。

public class User {

   public string ID {get;set;}
   public string FirstName {get; set;}
   public string LastName {get; set;}
   public string PhoneNo {get; set;}

  public AccountCollection accounts {get; set;}

  public User {
     accounts = new AccountCollection(this);
  }

  public static List<Users> GetUsers() {
     return Data.GetUsers();
  }

}

public AccountCollection : IEnumerable<Account> {
  private User user;

  public AccountCollection(User user) {
     this.user = user;
  }

  public IEnumerable<Account> GetEnumerator() {
     return Data.GetAccounts(user);
  }
}


public class Account {

   public User User {get; set;}  //This is public so that the subaccount can access its Account's User's ID
   public int ID;
   public string Name;

   public Account(User user) {
      this.user = user;
   }

}

public SubAccountCollection : IEnumerable<SubAccount> {
  public Account account {get; set;}

  public SubAccountCollection(Account account) {
     this.account = account;
  }

  public IEnumerable<SubAccount> GetEnumerator() {
     return Data.GetSubAccounts(account);
  }
}


public class SubAccount {
   public Account account {get; set;}    //this is public so that my Data class can access the account, to get the account's user's ID.

   public SubAccount(Account account) {
      this.account = account;  
   }

   public Report GenerateReport() {
       Data.GetReport(this);
   }

}


public static class Data {

  public static List<Account> GetSubAccounts(Account account) {

      using (var dc = new databaseDataContext()) {
          List<SubAccount> query = (from a in dc.Accounts
                                where a.UserID == account.User.ID  //this is getting the account's user's ID
                                select new SubAccount(account) {
                                    ID = a.ID,
                                    Name = a.Name,
                                }).ToList();
      }

  }

  public static List<Account> GetAccounts(User user) {

     using (var dc = new databaseDataContext()) {
         List<Account> query = (from a in dc.Accounts
                               where a.UserID == User.ID  //this is getting the user's ID
                               select new Account(user) {
                                   ID = a.ID,
                                   Name = a.Name,
                               }).ToList();
     }
  }

  public static Report GetReport(SubAccount subAccount) {

     Report report = new Report();
     //database access code here
     //need to get the user id of the subaccount's account for data querying.
     //i've got the subaccount, but how should i get the user id.
     //i would imagine something like this:
     int accountID = subAccount.Account.User.ID;
     //but this would require the subaccount's Account property to be public.
     //i do not want this to be accessible from my other project (UI).
     //reading up on internal seems to do the trick, but within my code it still feels
     //public. I could restrict the property to read, and only private set.

     return report;
  }

  public static List<User> GetUsers() {

     using (var dc = new databaseDataContext()) {
         var query = (from u in dc.Users
                     select new User {
                       ID = u.ID,
                       FirstName = u.FirstName,
                       LastName = u.LastName,
                       PhoneNo = u.PhoneNo
                     }).ToList();

         return query;
     }
  }

}
4

3 に答える 3

3

この回答には、多くの流行語の見出しが含まれています。うまくいけば、それぞれを説明し、なぜそれがここに当てはまるのかを説明します. 以下で紹介する各概念は検討する価値があると思います。それらは常に適用できるわけではありませんが、システムの構造を考えるときに個人的に価値があると思うものです。

単一の責任

まず、各オブジェクトの責任について考えることから始めます。その役割は何ですか? 通常、クラスごとに 1 つのジョブを決定すると、より良いデザインが見つかります。現在、多くのクラスがやりすぎており、実際にはサービスとして存在するはずのロジックを保持しています。

上記の最初の例は User クラスです。

public class User { 

   public string ID {get;set;} 
   public string FirstName {get; set;} 
   public string LastName {get; set;} 
   public string PhoneNo {get; set;} 

  public AccountCollection accounts {get; set;} 

  public User { 
     accounts = new AccountCollection(this); 
  } 

  public static List<Users> GetUsers() { 
     return Data.GetUsers(); 
  } 

} 

データ ソースからユーザーを取得するメソッドが提供されるのはなぜですか? その機能は、ユーザー サービスに移動する必要があります。

これのもう 1 つの重要な例は、SubAccount の GenerateReport メソッドです。レポート生成ロジックを SubAccount オブジェクトに強く結び付けないでください。これを分割することで、柔軟性が高まり、レポート ロジックを壊すサブアカウントへの変更を減らすことができます。

遅延読み込み

User クラスをもう一度見てください。インスタンス化時にすべてのユーザー アカウントをロードするのはなぜですか? これらのオブジェクトは、ユーザーと作業するたびに常に使用されますか?

通常は、遅延読み込みを導入する方がよいでしょう。必要なときにのみアカウントを取得します。もちろん、熱心な読み込みが必要な場合もありますが (オブジェクトがすぐに必要になることがわかっているため、データベースへのアクセスを減らしたい場合)、これらの例外に対応できるように設計する必要があります。

依存性注入

この種のものは、遅延読み込みポイントと単一責任ポイントの両方から派生しています。Data クラスのようなものへのハードコードされた参照がたくさんあります。これにより、設計がより厳格になります。データ アクセスをリファクタリングして遅延読み込みを導入したり、ユーザー レコードの取得方法を変更したりすることは、多くのクラスがすべてデータ アクセス ロジックに直接アクセスするようになったため、非常に難しくなっています。

ダイジェスト オブジェクト

Cade Roux へのハット チップ - ダイジェスト オブジェクトという用語を聞いたことがありませんでした。通常、それらは軽量 DTO と呼ばれていました。

Cade が言うように、一意の ID にバインドされたユーザー名のコンボ ボックスを表示するだけなら、完全に機能するユーザー オブジェクトを含むリッチ リストを取得する理由はありません。

非常に基本的なユーザー情報のみを格納する軽量のユーザー オブジェクトを導入します。

これも、サービス/リポジトリの抽象化とある種の依存性注入を導入するもう 1 つの理由です。実際のオブジェクトからデータの取得をカプセル化し、データ アクセスの実装に厳密に縛られていない場合、データ ストアから取得するオブジェクトの型を変更することは非常に簡単になります。

デメテルの法則

オブジェクトは、互いの内部構造についてあまりにも多くのことを知っています。ユーザーからアカウント、そしてサブアカウントへのドリルダウンを許可すると、各オブジェクトの責任が混乱します。おそらくそうすべきではない場合でも、ユーザーオブジェクトからサブアカウント情報を設定できます。

階層をドリルダウンするのは非常に便利に思えるので、私はいつもこの原則に苦労しています。問題は、各オブジェクトのカプセル化と役割について慎重に考えるのをやめてしまうことです。

おそらく、アカウント オブジェクトをユーザーから公開しないでください。代わりに、アカウント オブジェクトの関連メンバーを公開するプロパティとメソッドを導入してみてください。アカウント オブジェクトをユーザーのプロパティとして扱うのではなく、強制的に操作できるように、アカウント プロパティの代わりに GetAccount メソッドを見てください。

于 2010-04-02T03:55:10.403 に答える
2

遅延読み込み - アクセスされない限り、Users 内に AccountCollection オブジェクトを作成しないでください。または、ユーザーと同時にアカウント コレクションを取得し、1 + 9000 回のデータベース トリップを回避することもできます。

より選択的なコレクション検索方法を用意してください (つまり、9000 人のユーザーをどうするつもりですか? ユーザーのすべての情報が必要な場合、それほど無駄ではありません)。

ドロップダウンやルックアップなどの長いリストに使用される、より小さな「ダイジェスト」オブジェクトを用意します。これらのオブジェクトは、単純な読み取り専用のビジネス オブジェクトです。通常は少量の情報しか含まず、完全なオブジェクトを取得するために使用できます。

ページネーションを行っている場合、ユーザーのリストは一度ロードされて Web サーバーに保存され、キャッシュされたコピーからページ付けされますか?それともデータベースから毎回コレクションがロードされ、コントロールによってページ付けが行われますか?

于 2010-04-02T03:24:20.643 に答える
0

さて、現時点でのコードについての簡単なコメントです。

public class SubAccount {
   public Account account {get; set;}    //this is public so that my Data class can access the account, to get the account's user's ID.

    public SubAccount(Account account) {
        this.account = account;  
    }
    [snip]
}

Accountプロパティが ctor に渡され、バッキング フィールドに割り当てられている場合は、プロパティのセッターも必要ありません。

プロパティを ctor に渡して設定することは非常に良い方法ですが、データ オブジェクトがシリアル化される場合、つまり、データ オブジェクトが取得されるか Web サービスに送信される場合は失敗します。プロパティには少なくともinternalセッターが必要です。Account

編集: コンストラクターでこの DI 型の動作を引き続き使用できますが、問題は、オブジェクトがシリアル化されたフォームから再水和されるとき (つまり、Web サービスとの間でやり取りされるとき) に、このコンストラクターが呼び出されないことです。そのため、Web サービスを使用する場合は、パラメーターのないコンストラクターと、Account プロパティのパブリックまたは内部セッターが必要になります (内部セッターを使用する場合は、AssemblyInfo で も指定する必要がありますInternalsVisibleToAttribute) 。 .csファイル。

于 2010-04-02T23:17:00.273 に答える