2009-04-09 7 views
0

J'ai un système avec lequel je me bats depuis un moment. Essentiellement, il utilise beaucoup d'abstraction pour faire face au fait que l'extension ultérieure n'est pas seulement attendue, mais nécessaire. Un endroit où cela est requis est l'accès aux données. Le système traite généralement de la gestion d'objets encapsulant une observation (au sens d'une valeur observée ou d'un ensemble de valeurs) et de les utiliser. A cette fin, j'ai quelque chose à l'effet de:L'interface expose le type A, mais l'implémentation nécessite le type B (sous-classe de A)

public interface Observation 
{ 
    /** UniqueKey is used to access/identify an observation */ 
    UniqueKey Key 
    { 
     get; 
    } 
} 

public interface ObservationDataSource 
{ 
    /** 
     * Retrieves an Observation from the actual data source. 
     * performs any necessary operations to encapsulate values in object 
     */ 
    Observation GetObservationByUniqueKey(UniqueKey key); 
} 

Le problème se pose pour des implémentations spécifiques de ces interfaces. Finalement, les classes Observation et ObservationDataSource sont implémentées avec des classes d'exécution spécifiques. Cependant, UniqueKey peut également être étendu pour traiter n'importe quel ensemble de valeurs d'identification unique pour une observation dans la source de données (peut-être un identifiant, peut-être une heure, etc.). Ainsi, toute implémentation de GetObservationByUniqueKey exposera un argument UniqueKey, mais attend une sous-classe spécifique. Je pense que le UniqueKey sera casté à un type spécifique une fois passé.

Cela semble être un mauvais choix de conception, puisque l'implémentation repose sur les exigences de l'argument - mais je ne vois pas d'autre moyen de le faire. Je m'attends à ce que d'autres personnes utilisent ces interfaces, donc je ne peux pas dire que je me souviendrai de cette convention.

Des idées pour le réparer ou le manipuler plus élégamment?

+0

J'adore l'icône beartato! =) –

+0

haha, merci! J'aime ce petit homme. –

Répondre

0

Je ne vois pas cela comme un problème, simplement en modifiant celui de vos déclarations:

Ainsi, toute mise en œuvre de GetObservationByUniqueKey exposera un argument UniqueKey, mais attendez-vous une sous-classe spécifique si et seulement si cette UniqueKey a été générée par ce ObservationDataSource.

Si le UniqueKey n'est pas du type attendu, c'est juste un cas de rejet trivial qui peut être traité de deux façons:

(1) En exposant une propriété UniqueKeyType dans votre interface ObservationDataSource, l'appelant de GetObservationByUniqueKey peut vérifier le type d'instance de UniqueKey a priori. (2) Il incombe à chaque implémenteur de GetObservationByUniqueKey de gérer le cas où UniqueKey n'a pas été généré par lui. (Ceci me semble tout à fait raisonnable)

Cependant, votre question entière soulève la question - pourquoi permettez-vous d'UniqueKey d'être polymorphique en premier lieu, quand vous avez déjà une fonction de recherche définie pour obtenir à vos objets de données?

+0

Voulez-vous faire de UniqueKey une classe avec une sorte de collection qui contient toutes les valeurs clés (dans le cas d'une clé composite)? Ou ai-je manqué le point? –

+0

Je dis le contraire - UniqueKey devrait être une classe aussi simple que possible, je ne vois pas quel avantage réel de l'autoriser à hériter. –

0

Je ne sais pas si c'est ce que vous voulez, mais il me vous pouvez essayer de le faire semble avec les génériques:

public interface Observation<T> where T : UniqueKey 
{ 
    T Key { get; } 
} 

public interface ObservationDataSource<T> where T : UniqueKey 
{ 
    Observation<T> GetObservationByUniqueKey(T key); 
} 

Maintenant, l'interface est fortement typée avec la sous-classe spécifique de UniqueKey que votre classe exige.

0

Vous dites

Ainsi, toute mise en œuvre de GetObservationByUniqueKey exposera un argument UniqueKey,

Howerver, ce qui est faux. Cela n'exposera pas l'argument - il en recevra un. Comme vous le dites, l'appelant peut passer des clés uniques arbitraires, et il n'y a rien de mal à cela.

Pour une source de données spécifique, seules des clés uniques spécifiques sont associées à une observation - et pas seulement à une valeur spécifique. type, mais aussi spécifique. à la valeur réelle. Si la valeur n'a aucune observation associée, vous renvoyez null (je suppose). Faites de même si le type de l'ID unique est incorrect - si quelqu'un passe un moment où un ID est attendu, aucune observation n'est associée à cette clé, dans cette source de données.

OTOH, si l'appelant est libre de choisir ids uniques, et la source de données est censée retourner une observation vide à remplir par l'appelant, vous avez deux choix:

  1. Ne pas utiliser la interfaces en premier lieu. Apparemment, vous ne pouvez pas substituer une implémentation à l'autre, et l'appelant doit savoir quelle implémentation il utilise.
  2. Vous pouvez également vous assurer que toutes les implémentations prennent en charge toutes sortes de clés uniques.
Questions connexes