103

OOPについてどんどん学び、さまざまなデザインパターンを実装し始めると、人々がActiveRecordを嫌っているケースに戻ってきます。

多くの場合、人々はそれがうまくスケーリングしないと言います(彼らの主要な例としてTwitterを引用します)-しかし、なぜそれがうまくスケーリングしないのか実際には誰も説明しません。および/または短所なしでARの長所を達成する方法(類似しているが異なるパターンを介して?)

うまくいけば、これがデザインパターンについての聖戦にならないことを願っています-私が知りたいのは、****具体的に****ActiveRecordの何が問題なのかということだけです。

うまくスケーリングしない場合は、なぜですか?

他にどのような問題がありますか?

4

14 に答える 14

91

ActiveRecordのデザインパターンActiveRecordのRailsORMライブラリがあり、.NETやその他の言語のノックオフもたくさんあります。

これらはすべて異なるものです。それらは主にそのデザインパターンに従いますが、さまざまな方法で拡張および変更するため、「ActiveRecord Sucks」と言う前に、「どのActiveRecord、ヒープがありますか?」と言うことで修飾する必要があります。

私はRailsのActiveRecordに精通しているだけなので、それを使用する際に提起されたすべての苦情に対処しようとします。

@BlaM

Active Recordsで見られる問題は、それが常に約1つのテーブルであるということです。

コード:

class Person
    belongs_to :company
end
people = Person.find(:all, :include => :company )

これにより、でSQLが生成されLEFT JOIN companies on companies.id = person.company_id、関連付けられたCompanyオブジェクトが自動的に生成されるpeople.first.companyため、データが既に存在するため、データベースにアクセスする必要がありません。

@ pix0r

Active Recordに固有の問題は、データベースクエリが自動的に生成および実行されて、オブジェクトにデータが入力され、データベースレコードが変更されることです。

コード:

person = Person.find_by_sql("giant complicated sql query")

これは醜いのでお勧めできませんが、単純で生のSQLを作成する必要がある場合は、簡単に実行できます。

@ティムサリバン

...そしてモデルのいくつかのインスタンスを選択すると、基本的に「select*from...」を実行します。

コード:

people = Person.find(:all, :select=>'name, id')

これにより、データベースから名前とID列のみが選択され、マップされたオブジェクトの他のすべての「属性」は、そのオブジェクトを手動で再ロードしない限り、nilになります。

于 2008-08-11T21:02:03.870 に答える
55

ActiveRecord は、モデルが比較的フラットな CRUD ベースの迅速なアプリケーションに適していることが常にわかっています (クラス階層があまりない場合など)。ただし、複雑な OO 階層を持つアプリケーションの場合、おそらくDataMapperの方が優れたソリューションです。ActiveRecord は、テーブルとデータ オブジェクトの比率を 1:1 と想定していますが、そのような関係は、より複雑なドメインでは扱いにくくなります。Martin Fowler は、彼の著書 patternで、モデルがかなり複雑な状況では ActiveRecord が機能しなくなる傾向があることを指摘し、代わりにDataMapperを提案しています。

私はこれが実際に真実であることを発見しました。ドメインに多くの継承がある場合、関連付けや構成をマップするよりも、継承を RDBMS にマップする方が難しい場合があります。

私が行う方法は、これらの DataMapper (または「サービス層」) クラスを介してコントローラーからアクセスされる「ドメイン」オブジェクトを用意することです。これらはデータベースを直接ミラーリングするのではなく、実際のオブジェクトの OO 表現として機能します。ドメインに User クラスがあり、その User オブジェクトを取得するときに、他のオブジェクトへの参照またはコレクションが既に読み込まれている必要があるとします。データは多くの異なるテーブルから取得される可能性があり、ActiveRecord パターンはそれを非常に困難にする可能性があります。

ユーザー オブジェクトを直接ロードし、ActiveRecord スタイルの API を使用してデータにアクセスする代わりに、コントローラー コードは、たとえば UserMapper.getUser() メソッドの API を呼び出してユーザー オブジェクトを取得します。関連するオブジェクトをそれぞれのテーブルからロードし、完成したユーザー「ドメイン」オブジェクトを呼び出し元に返すのは、そのマッパーです。

基本的に、コードをより管理しやすくするために、別の抽象化レイヤーを追加しているだけです。DataMapper クラスに未加工のカスタム SQL が含まれているか、データ抽象化レイヤー API への呼び出しが含まれているか、または ActiveRecord パターン自体にアクセスするかどうかは、データが入力された適切な User オブジェクトを受け取るコントローラー コードにとっては重要ではありません。

とにかく、それは私がそれを行う方法です。

于 2008-08-26T18:10:08.283 に答える
11

人々がActiveRecordを「嫌っている」理由と、それを「間違っている」理由との間には、おそらく非常に異なる一連の理由があると思います。

嫌いな問題については、Railsに関連するあらゆるものに対して多くの毒があります。何が悪いのかというと、それはすべてのテクノロジーのようであり、それが良い選択である状況と、より良い選択がある状況があります。私の経験では、Rails ActiveRecordのほとんどの機能を利用できない状況は、データベースの構造が不適切な場合です。データにアクセスするために必要なストアドプロシージャが多数ある、第一正規形に違反するもので主キーなしでデータにアクセスする場合は、単なるSQLラッパーではないものを使用することをお勧めします。データベースが比較的適切に構造化されている場合、ActiveRecordを使用するとそれを利用できます。

コードスニペットリジョインダーを使用して、ActiveRecordで物事が難しいと言っているコメント投稿者に返信するというテーマに追加します

@Sam McAfeeドメインにUserクラスがあり、そのUserオブジェクトを取得するときに、他のオブジェクトへの参照またはコレクションがすでにロードされている必要があるとします。データは多くの異なるテーブルからのものである可能性があり、ActiveRecordパターンはそれを非常に困難にする可能性があります。

user = User.find(id, :include => ["posts", "comments"])
first_post = user.posts.first
first_comment = user.comments.first

includeオプションを使用すると、ActiveRecordでデフォルトの遅延読み込み動作をオーバーライドできます。

于 2008-09-22T21:35:07.883 に答える
9

私の長くて遅い答えは完全ではありませんが、なぜ私がこのパターン、意見、さらにはいくつかの感情を嫌うのかについての良い説明です。

1) 短いバージョン: Active Record は、データベースとアプリケーション コードの間に「強力な結合」の「薄い層」を作成します。これは、論理的な問題も、何の問題も解決せず、まったく問題を解決しません。私見では、プログラマー向けの構文糖衣を除いて、任意の値を提供しません(リレーショナルデータベースに存在するデータにアクセスするために「オブジェクト構文」を使用する場合があります)。プログラマーに快適さを提供するための努力は (IMHO...) 低レベルのデータベース アクセス ツールに投資する方がよいでしょう。使用言語)。hash_map get_record( string id_value, string table_name, string id_column_name="id" )

2) 長いバージョン: 私が物事の「概念的な制御」を行っていたデータベース駆動型のプロジェクトでは、AR を避けましたが、それは良かったです。私は通常、レイヤード アーキテクチャを構築します (少なくとも中規模から大規模のプロジェクトでは、遅かれ早かれソフトウェアをレイヤーに分割します)。

A1) データベース自体、テーブル、リレーション、さらには DBMS で許可されている場合はロジック (MySQL も現在は成熟しています)

A2) 非常に多くの場合、データ ストア以上のものがあります。ファイル システム (データベース内の BLOB が常に適切な決定であるとは限りません...)、レガシー システム (アクセスされる "方法" を想像してみてください。多くの種類が考えられます。..重要ではありません...)

B) データベース アクセス レイヤー (このレベルでは、ツール メソッド、データベース内のデータに簡単にアクセスするためのヘルパーは大歓迎ですが、AR は、いくつかの構文糖衣を除いて、ここでは何の価値も提供しません)

C) アプリケーション オブジェクト レイヤー: 「アプリケーション オブジェクト」は、データベース内のテーブルの単純な行である場合もありますが、ほとんどの場合、とにかく複合オブジェクトであり、より高度なロジックが付加されているため、このレベルで AR オブジェクトに時間を費やすことは明らかに役に立ちません。 、貴重なコーダーの時間の無駄です。なぜなら、これらのオブジェクトの「真の価値」、「より高いロジック」は、とにかく、AR オブジェクトの上に実装する必要があるからです - AR の有無にかかわらず! たとえば、「ログ エントリ オブジェクト」を抽象化する必要があるのはなぜでしょうか。アプリのロジック コードはそれらを書き込みますが、それらを更新または削除する機能が必要ですか? ばかげているように聞こえApp::Log("I am a log message")ますが、よりもはるかに使いやすいです。le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();. たとえば、アプリケーションのログ ビューで「ログ エントリ オブジェクト」を使用すると、100、1000、または 10000 のログ行でも機能しますが、遅かれ早かれ最適化する必要があります。ほとんどの場合、アプリのロジックでその小さな美しい SQL SELECT ステートメントを使用してください (AR のアイデアを完全に壊してしまいます..)。その小さなステートメントを厳格な固定 AR アイデア フレームにラップし、多くのコードをラップして隠します。AR コードの作成や作成に費やした時間は、ログ エントリのリストを読み取るためのはるかに優れたインターフェイスに費やされた可能性があります (多くの方法で、空は限界です)。コーダーは、意図したアプリケーションに適合するアプリケーション ロジックを実現するために、あえて新しい抽象化を発明する必要があります。、一目でいいですね!

D) アプリケーション ロジック - オブジェクトを操作し、アプリケーション ロジック オブジェクトを作成、削除、および一覧表示するロジックを実装します (いいえ、これらのタスクがアプリケーション ロジック オブジェクト自体に固定されることはめったにありません。オフィス内の他のすべてのシートの名前と場所を知っていますか?オブジェクトをリストするための「静的な」方法を忘れてください、それはばかげて、人間の考え方を [some-not-all-AR-framework-like] -]AR思考)

E) ユーザー インターフェイス - ええと、次の行で書くことは非常に、非常に、非常に主観的ですが、私の経験では、AR で構築されたプロジェクトは、アプリケーションの UI 部分を無視することがよくありました - あいまいな抽象化の作成に時間が浪費されていました. 結局、そのようなアプリケーションは多くのコーダーの時間を無駄にし、コーダーからコーダーのためのアプリケーション、内外の技術に傾倒したアプリケーションのように感じました。コーダーは気分が良く (懸命な作業が最終的に完了し、すべてが完成し、紙のコンセプトに従って正しい...)、顧客は「そのようにする必要があることを学ぶ必要があります」。それが「プロフェッショナル」だからです..わかりました、すみません、脱線します;-)

確かに、これはすべて主観的なものですが、それは私の経験です (Ruby on Rails は除外されます。異なる可能性があります。また、そのアプローチに関する実際の経験はありません)。

有償プロジェクトでは、より高いレベルのアプリケーション ロジックのビルディング ブロックとして、いくつかの「アクティブ レコード」オブジェクトを作成することから始めるという要求をよく耳にします。私の経験では、これは際立って頻繁に顧客 (ほとんどの場合、ソフトウェア開発会社) が、製品が最終的にどうあるべきかについての優れたコンセプト、全体像、概要を持っていなかったことに対する、ある種の言い訳でした。それらの顧客は、厳格な枠組みで考え (「10 年前のプロジェクトではうまくいきました..」)、エンティティを肉付けし、エンティティの関係を定義し、データの関係を分解し、基本的なアプリケーション ロジックを定義するかもしれませんが、その後停止します。そしてそれをあなたに手渡し、必要なものはそれだけだと考えてください...彼らはしばしばアプリケーションロジック、ユーザーインターフェース、使いやすさなどの完全な概念を欠いています...彼らは全体像を欠き、詳細を説明し、彼らはあなたにその AR のやり方に従うことを望んでいます。なぜなら、何年も前にそのプロジェクトで機能したのに、人々は忙しくて沈黙していたからです。知らない。しかし、「詳細」男性と男の子を区別する、または..元の広告スローガンはどうでしたか? ;-)

長年 (10 年間の積極的な開発経験) を経て、顧客が「アクティブな記録パターン」について言及するたびに、私の警報ベルが鳴ります。私は彼らをその本質的な概念段階に戻そうとすることを学びました。彼らによく考えさせ、彼らの概念上の弱点を示すように試みます。または、彼らが見分けがつかない場合はまったく避けます (最終的には、まだ理解していない顧客です)。それが何を望んでいるのかを知っている、多分知っているが知らないと思っている、またはコンセプトワークを無料で私に外部化しようとしている、貴重な時間、日、週、月の時間を費やしている、ライブは短すぎる...)。

最後に、これが私がそのばかげた「アクティブ レコード パターン」を嫌う理由です。

編集:私はこれを No-Pattern とさえ呼びます。それは何の問題も解決しません (パターンはシンタックス シュガーを作成するためのものではありません)。それは多くの問題を引き起こします: そのすべての問題の根源 (ここで多くの回答で言及されています..) は、古き良きよく開発された強力な SQL を、パターン定義によって非常に制限されたインターフェイスの背後に隠していることです。

このパターンは、柔軟性をシンタックス シュガーに置き換えます。

考えてみてください。AR はどのような問題を解決してくれますか?

于 2009-11-27T09:02:16.343 に答える
6

いくつかのメッセージが私を混乱させています。いくつかの答えは、「ORM」対「SQL」またはそのようなものになります。

実際のところ、AR は単純化されたプログラミング パターンにすぎず、ドメイン オブジェクトを利用してそこにデータベース アクセス コードを記述します。

これらのオブジェクトには通常、ビジネス属性 (Bean のプロパティ) といくつかの動作 (これらのプロパティで通常動作するメソッド) があります。

AR は、データベース関連のタスクに「これらのドメイン オブジェクトにいくつかのメソッドを追加する」とだけ言っています。

そして、私の意見と経験から、私はそのパターンが好きではないと言わざるを得ません.

一見すると、かなり良い音に聞こえるかもしれません。Spring Roo などの一部の最新の Java ツールでは、このパターンが使用されています。

私にとって、本当の問題は OOP の問題です。AR パターンは、何らかの方法で、オブジェクトからインフラストラクチャ オブジェクトへの依存関係を追加することを強制します。これらのインフラストラクチャ オブジェクトにより、ドメイン オブジェクトは AR によって提案された方法でデータベースにクエリを実行できます。

私は常々、プロジェクトの成功には 2 つのレイヤーが重要であると言っています。サービス レイヤー (ビジネス ロジックが存在する場所、または Web サービスなどの何らかのリモート テクノロジを介してエクスポートできる場所) とドメイン レイヤー。私の意見では、AR パターンを解決するためにドメイン レイヤー オブジェクトにいくつかの依存関係 (実際には必要ありません) を追加すると、ドメイン オブジェクトを他のレイヤーまたは (まれな) 外部アプリケーションと共有することが難しくなります。

AR の Spring Roo 実装は、オブジェクト自体に依存するのではなく、いくつかの AspectJ ファイルに依存するため、興味深いものです。しかし、後で Roo を使用したくなくなり、プロジェクトをリファクタリングする必要が生じた場合、AR メソッドはドメイン オブジェクトに直接実装されます。

別の視点。オブジェクトを格納するためにリレーショナル データベースを使用しないとします。たとえば、アプリケーションがドメイン オブジェクトを NoSQL データベースまたは単に XML ファイルに格納するとします。これらのタスクを実行するメソッドをドメイン オブジェクトに実装しますか? 私はそうは思いません (たとえば、XM の場合、XML 関連の依存関係をドメイン オブジェクトに追加することになります...本当に悲しいことだと思います)。では、Ar パターンが示すように、ドメイン オブジェクトにリレーショナル DB メソッドを実装する必要があるのはなぜでしょうか?

要約すると、AR パターンはよりシンプルに聞こえ、小規模で単純なアプリケーションに適しています。しかし、複雑で大規模なアプリがある場合は、従来のレイヤード アーキテクチャの方が優れたアプローチだと思います。

于 2011-08-25T09:42:35.590 に答える
3

質問は、アクティブ レコードのデザイン パターンに関するものです。orm ツールではありません。

元の質問は rails でタグ付けされており、Ruby on Rails に組み込まれている Twitter を参照しています。Rails 内の ActiveRecord フレームワークは、Fowler の Active Record デザイン パターンの実装です。

于 2008-08-26T19:19:25.370 に答える
2

Active Recordに関する苦情に関して私が見た主なことは、テーブルの周りにモデルを作成し、モデルのいくつかのインスタンスを選択すると、基本的に「select*from...」を実行しているということです。これは、レコードの編集やレコードの表示には問題ありませんが、たとえば、データベース内のすべての連絡先の都市のリストを表示する場合は、「...から都市を選択」を実行して都市のみを取得できます。 。Active Recordでこれを行うには、すべての列を選択する必要がありますが、Cityのみを使用します。

もちろん、実装を変えると、これは異なる方法で処理されます。それにもかかわらず、それは1つの問題です。

今、あなたはあなたがやろうとしている特定のことのために新しいモデルを作成することによってこれを回避することができます、しかし何人かの人々はそれが利益よりも努力であると主張するでしょう。

私、ActiveRecordを掘ります。:-)

HTH

于 2008-08-11T16:22:52.967 に答える
2

SQL 最適化に関する他のすべてのコメントは確かに有効ですが、アクティブ レコード パターンに関する私の主な不満は、通常はインピーダンスの不一致につながるということです。私は自分のドメインをきれいに保ち、適切にカプセル化するのが好きですが、アクティブなレコード パターンは通常、それを行うすべての希望を破壊します。

于 2008-08-26T18:31:26.623 に答える
1

SubSonicが1列だけのことを行う方法が大好きです。
また

DataBaseTable.GetList(DataBaseTable.Columns.ColumnYouWant)

、 また:

Query q = DataBaseTable.CreateQuery()
               .WHERE(DataBaseTable.Columns.ColumnToFilterOn,value);
q.SelectList = DataBaseTable.Columns.ColumnYouWant;
q.Load();

しかし、遅延読み込みに関しては、Linqは依然として王様です。

于 2008-08-11T19:50:26.293 に答える
1

@BlaM:時々、結合の結果に対してアクティブレコードを実装するだけでした。必ずしも関係テーブル<->アクティブレコードである必要はありません。「Joinステートメントの結果」<->ActiveRecordはどうですか?

于 2008-08-11T21:14:48.870 に答える
1

設計パターンとして Active Record について話しますが、ROR は見たことがありません。

一部の開発者は Active Record を嫌っています。なぜなら、彼らはきれいできちんとしたコードを書くことについての賢明な本を読んでいるからです。これらの本は、Active Record は単一の責任の原則に違反し、ドメイン オブジェクトは永続的無知であるべきであるという DDD 規則に違反し、これらの種類の本からの他の多くの規則に違反すると述べています。 .

次に、Active Record のドメイン オブジェクトはデータベースと 1 対 1 である傾向があります。これは、ある種のシステム (主に n 層) では制限と見なされる場合があります。

それは単なる抽象的なものです。このパターンの実際の実装をルビー オン レールで見たことがありません。

于 2011-06-13T07:26:16.290 に答える
0

Active Recordsで見られる問題は、それが常に約1つのテーブルであるということです。その1つのテーブルだけを実際に操作する限り、それは問題ありませんが、ほとんどの場合、データを操作するときは、どこかで何らかの結合が行われます。

はい、通常、結合はパフォーマンスに関してはまったく結合しないよりも悪いですが、結合 は通常、最初にテーブルA全体を読み取り、次に取得した情報を使用してテーブルBを読み取り、フィルタリングすることにより、 「偽の」結合よりも優れています。

于 2008-08-11T16:46:14.917 に答える
0

ActiveRecord の問題は、自動的に生成されるクエリがパフォーマンスの問題を引き起こす可能性があることです。

クエリを最適化するためにいくつかの非直感的なトリックを実行することになり、そもそもクエリを手動で記述した方が時間効率が良かったのではないかと疑問に思うことになります。

于 2008-08-15T14:32:24.233 に答える
0

多対多のポリモーフィックな関係を試してみてください。それほど簡単ではありません。特にSTIを使用していない場合。

于 2009-07-20T13:39:29.437 に答える