Question

Supposons que j'ai les types de données suivantes:

class Customer {
  String id; // unique
  OtherCustData someOtherData;
}

class Service {
  String url; // unique
  OtherServiceData someOtherData;
}

class LastConnection {
  Date date;
  OtherConnData someOtherData; // like request or response
}

Maintenant, je dois me rappeler quand chacun des clients connectés à chacun des services.
Je voudrais faire la structure:

Map<Customer, Map<Service, LastConnection>> lastConnections;

Ou, pour être en mesure de rechercher par ids et ne pas avoir à écrire tous les égaux () et hashCode ():

Map<String, Map<String, LastConnection>> lastConnections;

Maintenant, je pouvais accéder aux données de LastConnection par

LastConnection connection = lastConnections.get(custId).get(srvUrl);

Tout cela semble laid, surtout que je dois passer en tant que paramètres à des dizaines de méthodes de cartes carte attente de LastConnections, donc je pense à créer mes propres classes qui ressemblerait à quelque chose comme ça:

class CustomerConnections extends HashMap<String, LastConnection> {
}

class AllConnections extends HashMap<String, CustomerConnections> {
    public LastConnection get(String custId, String srvUrl) {
        return get(custId).get(srvUrl);
    }
}

Ok, j'ai appris déjà que l'héritage est 3v1l, donc nous allons essayer composition:

class CustomerConnections {
    Map<String, LastConnection> customerConnections;
    LastConnection get(String srvUrl) { 
        return customerConnections.get(srvUrl);
    }
    ... // all other needed operations;
}

class AllConnections {
    Map<String, CustomerConnections> allConnections;
    public LastConnection get(String custId, String srvUrl) {
        return get(custId).get(srvUrl);
    }
    public CustomerConnection get(String custId) {
        return allConnections.get(custId);
    }
    ... // all other needed operations;
}

Le problème est que je ne suis pas sûr de ce que serait la meilleure approche en respectant les principes et toutes les meilleures pratiques solides. Création de classes qui ne font rien, sauf extension des collections déjà existantes semble que de multiplier les entités au-delà de la nécessité, mais rendrait mon code plus clair (Surtout quand il y a des niveaux suivants - comme carte de AllConnections par mois et ainsi de suite). Toutes les directions?

Était-ce utile?

La solution

  

Création de classes qui ne font rien   sauf extension déjà existante   collections semble multiplier comme   entités au-delà de la nécessité

Je changerais étendre à encapsuler. Vous cachez les détails de la façon dont ces informations sont stockées. Les clients de votre classe ne ont pas besoin de savoir comment vous leur fournissez l'historique de connexion à la clientèle. Je pense que cela est une bonne idée puisque vous pouvez changer le modèle sous-jacent sans clients du api changent leur code.

  

mais rendrait mon code plus clair

est grand et est une bonne raison de le faire. YourClass.getCustomerConnection (cid) est beaucoup plus claire que yourCollection.get (id) .get (id) .getConnection (). Vous devez rendre la vie des personnes utilisant ce code plus facile, même si vous êtes cette personne.

  

(Surtout quand il y a des niveaux suivants - comme carte de AllConnections par mois et ainsi de suite)

Bon, alors vous planifier à l'avance et de faire votre extensible de code. Ce qui est une bonne pratique de OO. À mon avis, la Conculsion vous avez atteint votre propre est ce que je ferais.

Autres conseils

Je voudrais créer un objet dédié pour stocker ces informations. Qu'est-ce que vous créez est un Gestionnaire d'objets , plutôt qu'une simple collection.

ne serait pas faire dériver d'une carte ou d'une autre classe de collection bien connue, puisque la sémantique de la façon dont vous stockez cette information peut changer à l'avenir.

mettre en œuvre au lieu d'une classe qui relie un client et sa connexion, et à l'intérieur de cette classe utiliser la classe de collecte approprié (que vous êtes libre de changer plus tard sans affecter l'interface et le reste de votre code)

Votre classe gestionnaire de client / de connexion est plus qu'un simple conteneur. Il peut stocker des méta-données (par exemple quand cette relation établie). Il peut effectuer des recherches sur les connexions données d'information à la clientèle (si vous avez besoin). Il peut gérer les doublons comment vous avez besoin, plutôt que de la façon dont la classe de collection sous-jacente les poignées etc etc Vous pouvez facilement fente dans le débogage / l'enregistrement / le suivi des performances de comprendre facilement ce qui se passe.

Je pense que vous devriez également considérer la façon dont les collections vont être utilisées et comment les données sont obtenues:

  • Si elles sont le résultat simples ensembles à afficher sur une page (par exemple), puis en utilisant des collections standards semble raisonnable - et vous pouvez alors utiliser beaucoup de bibliothèques standard pour les manipuler
  • .
  • Par contre, si elles sont mutable et des modifications doivent être persisté (par exemple), puis les encapsuler au sein de vos propres classes peut être préférable (qui peut alors écrire des modifications à des bases de données, etc.).

Comment obtenez-vous et persistez vos données? Si elle est stockée dans une base de données, alors peut-être que vous pouvez utiliser SQL pour sélectionner LastConnections par client et / ou d'un service et / ou par mois, plutôt que d'avoir à maintenir les structures de données vous (et il suffit de retourner les listes simples ou des cartes de connexions ou compte de connexions ). Ou peut-être que vous ne voulez pas exécuter une requête pour chaque demande, il ne faut garder l'ensemble en mémoire structure de données.

Encapsulation est généralement une bonne chose si, en particulier car il peut vous aider à adhérer à la loi de Déméter - peut-être vous pouvez pousser quelques-unes des opérations que vous effectuez sur les collections de nouveau dans la classe AllConnections (qui est effectivement un OTI). Cela peut aider souvent les tests unitaires.

En outre, pourquoi étend HashMap considéré comme le mal ici, étant donné que vous voulez seulement ajouter une méthode d'aide trivial? Dans votre code pour AllConnections, AllConnections se comportera toujours de la même manière que HashMap - il est polymorphically substituable. Bien sûr, vous êtes potentiellement vous enfermer dans l'utilisation HashMap (plutôt que TreeMap, etc.), mais cela n'a probablement pas, car il a les mêmes méthodes publiques que la carte. Cependant, si vous voulez vraiment faire cela ne dépend vraiment de la façon dont vous avez l'intention d'utiliser les collections - Je ne pense pas que vous devez automatiquement l'héritage d'implémentation rabais toujours mauvais (il est juste généralement !)

class AllConnections extends HashMap<String, CustomerConnections> {
    public LastConnection get(String custId, String srvUrl) {
        return get(custId).get(srvUrl);
    }
}

Why not use custId+"#"+srvurl as a key in a simple Map<String, LastConnection>?

Or use a Tuple or Pair class as key that contains the two IDs and implements hashCode() and equals() - the "cleanest" OO solution.

Why are you maintaining the relationships between objects outside of those objects.

I'd suggest something like the following:

public class Customer 
{
  public List<LastConnection> getConnectionHistory() 
  {
    ... 
  }

  public List<LastConnection> getConnectionHistory(Service service) 
  {
    ...
  }

  public List<LastConnection> getConnectionHistory(Date since) 
  {
    ...
  }
}

public class LastConnection
{
  public Date getConnectionTime() 
  {
    ...
  }

  public Service getService()
  {
    ...
  }
}

You then pass List<Customer> to the methods that use this information.

You could create a class that implements Map<K,V>, and internally delegate to a container map:

class CustomerConnections implements Map<String,LastConnection> {
    private Map<String, LastConnection> customerConnections;

    @Override
    public LastConnection get(Object srvUrl) { 
        return customerConnections.get(srvUrl);
    }
    // all other needed operations;
}

The nice thing with this approach is that you can pass around a Map which has a standard, well-defined contract, but you avoid extending library classes, which is often frowned upon.

EDIT : As pointed out below, this does have the downside of requiring you to implement a lot of methods that do nothing short of delegate to the underlying collection

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top