コードの設計についてアドバイスを求めています。
序章
いくつかのクラスがあり、それぞれが 1 つのファイル タイプを表します。たとえば、MediaImageFile、MediaAudioFile、ジェネリック (および基本クラス) MediaGenericFile です。
各ファイルには、Master と Version の 2 つのバリアントがあるため、これらのクラスを作成して、特定の動作を定義しました。編集:バージョンは、マスター ファイルのサイズ変更/クロップ/トリミング/その他のバリアントを表します。主にプレビュー用に使用されます。
編集: 動的に実行したい理由は、このアプリが再利用可能 (Django アプリ) であるため、元のコードを変更せずに他の MediaGenericFile サブクラスを簡単に実装できるはずだからです。
私がしたいこと
まず第一に、ユーザーは元のコードに影響を与えることなく、独自の MediaGenericFile サブクラスを登録できる必要があります。
ファイルがバージョンかマスターかは、ファイル名から簡単に (1 つの正規表現で) 認識できます。
/path/to/master.jpg -- master /path/to/.versions/master_version.jpg -- version
マスター/バージョン クラスは、ファイル名など、MediaGenericFile のいくつかのメソッド/プロパティを使用します (新しいバージョンを生成するにはファイル名を知る必要があります)。
MediaGenericFile は、LazyFile オブジェクトである LazyFile を拡張します。
今、それをまとめる必要があります…</p>
中古デザイン
「バージョン」機能のコーディングを開始する前に、拡張子に応じて適切なファイル タイプ クラスを返すファクトリ クラス MediaFile がありました。
>>> MediaFile('path/to/image.jpg')
<<< <MediaImageFile 'path/to/image.jpg'>
クラス Master と Version は、MediaGenericFile などのメソッドと属性を使用する新しいメソッドを定義します。
アプローチ1
1 つのアプローチは、マスター (またはバージョン) と MediaGenericFile (またはサブクラス) を継承する新しい型を動的に作成することです。
class MediaFile(object):
def __new__(cls, *args, **kwargs):
... # decision about klass
if version:
bases = (Version, klass)
class_name = '{0}Version'.format(klass.__name__)
else:
bases = (Master, klass)
class_name = '{0}Master'.format(klass.__name__)
new_class = type(class_name, bases, {})
...
return new_class(*args, **kwargs)
アプローチ 2
2 番目のアプローチは、Master/Version でメソッド 'contribute_to_instance' を作成し、new_class を作成した後にそれを呼び出すことですが、これは思ったよりもトリッキーです。
classs Master(object):
@classmethod
def contribute_to_instance(cls, instance):
methods = (...)
for m in methods:
setattr(instance, m, types.MethodType(getattr(cls, m), instance))
class MediaFile(object):
def __new__(*args, **kwargs):
... # decision about new_class
obj = new_class(*args, **kwargs)
if version:
version_class = Version
else:
version_class = Master
version_class.contribute_to_instance(obj)
...
return obj
ただし、これは機能しません。マスター/バージョンのメソッドの呼び出しにはまだ問題があります。
質問
この多重継承を実装する良い方法は何でしょうか?
この問題はどのように呼ばれますか? :)私はいくつかの解決策を見つけようとしていましたが、この問題に名前を付ける方法がわかりません。
前もって感謝します!
回答への注意
広告ラースマン
私の場合、比較とインスタンスチェックは問題になりません。
とにかく比較は再定義されます
class MediaGenericFile(object): def __eq__(self, other): return self.name == other.name
isinstance(MediaGenericFileVersion, instance) をチェックする必要はありません。isinstance(MediaGenericFile, instance) と isinstance(Version, instance) を使用していますが、どちらも期待どおりに動作します。
それにもかかわらず、インスタンスごとに新しいタイプを作成することは、かなりの欠陥のように思えます。
さて、メタクラスで両方のバリエーションを動的に作成してから、次のように使用できます。
>>> MediaGenericFile.version_class
<<< <class MediaGenericFileVersion>
>>> MediaGenericFile.master_class
<<< <class MediaGenericFileMaster>
その後:
class MediaFile(object):
def __new__(cls, *args, **kwargs):
... # decision about klass
if version:
attr_name = 'version_class'
else:
attr_name = 'master_class'
new_class = getattr(klass, attr_name)
...
return new_class(*args, **kwargs)
最終的解決
最後に、デザインパターンはファクトリークラスです。MediaGenericFile サブクラスは静的に型付けされ、ユーザーは独自に実装および登録できます。マスター/バージョン バリアントはメタクラスで動的に作成され (いくつかの mixin から結合されて)、 larsmansによって言及された危険を回避するために「キャッシュ」に格納されます。
みんなの提案に感謝します。最後に、メタクラスの概念を理解しました。まあ、少なくとも私はそれを理解していると思います。送信元マスターをプッシュ…</p>