2016-09-18 1 views
1

Je veux concevoir une API qui lit un fichier texte volumineux, extrait les informations pertinentes et retourne une liste des Foo objets comme celui-ci:architecture de services Java

interface FooService { 
    Optional<Foo> getFoo(Bar bar); 
} 

Le format du fichier texte et la façon dont il est analysé est toujours le même. La seule chose qui peut varier est l'emplacement du fichier, c'est-à-dire qu'il peut s'agir d'un fichier sur le système local ou d'une URL. J'ai donc créé un AbstractFooService:

class AbstractFooService implements FooService { 

    Map<Bar, Foo> registry; 

    AbstractFooService(InputStream is) { 
     try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) { 
      registry = reader.lines() 
       .map(l -> l.split(';')) 
       .map(a -> new Foo(a[0]), a[1])) 
       .collect(Collectors.groupingBy(...)); 
     } catch (IOException e) { 
      throw new UncheckedIOException(e); 
     } 
    } 

    Optional<Foo> getFoo(Bar bar) { 
     return Optional.ofNullable(registry.get(bar)); 
    } 
} 

implémentations concrètes prendriez simplement appeler le super constructeur avec un InputStream:

class UrlFooService extends AbstractFooService { 
    UrlFooService(String url) { 
     super(createStream(url)); 
    } 

    private static InputStream createStream(final String url) { 
     try { 
      return new URL(string).openStream(); 

     } catch (IOException e) { 
      throw new UncheckedIOException(e); 
     } 
    } 
} 

Est-ce une conception API sonore ou est-il un moyen « mieux » pour atteindre mon objectif ? C'est à dire. est-il intelligent d'appeler le super constructeur avec un InputStream ou serait-il préférable d'avoir une méthode séparée load() qui ouvre le flux en cas de besoin?

+3

Pour le code de travail, veuillez passer à codereview.stackexchange.com – GhostCat

+0

Faire un travail dans un constructeur est généralement une mauvaise idée. Il est difficile de tester le code et peut entraîner un comportement non évident dans les hiérarchies d'héritage. – sisyphus

Répondre

2

Je ne vois pas pourquoi vous avez besoin de cette classe de base abstraite. Préférant la composition à l'héritage; Je pense que la solution la plus raisonnable serait d'avoir:

public class FooServiceImpl implements FooService { 
... 

Et des clients tels que

public class UrlFooService implements FooService { 
    private final FooService delegatee; 

public UrlFooService(URL url) { 
    delegate = new FooServiceImpl(url.openStream()) 
... 
@Override 
Optional<Foo> getFoo(Bar bar) { return delegatee.getFoo(bar); } 

couples héritage vos classes de service en béton avec cette classe mère; Je préférerais éviter, en utilisant ce simple mécanisme "délégataire".

Veuillez noter: J'ai également changé le ctor de l'UrlSerivce pour prendre une URL. Vous avez déjà les types là-bas, alors pourquoi prendre la peine d'appeler vous-même? Cela signifie simplement que votre UrlService devra gérer toutes les choses qui pourraient mal tourner là-bas!

0

Il y a quelques problèmes avec votre question ici et je commencerais par décomposer le problème un peu différemment. Comme l'a dit @sisyphus, faites attention aux choses que vous faites dans le constructeur. Un constructeur devrait vraiment se concentrer sur la création d'un "objet valide" et rien de plus. Beaucoup de bonnes idées de @GhostCat aussi.

pense qu'au lieu de modéliser le problème comme suit:

Créer une interface qui représente l'API pour votre service. Dans ce cas si vous voulez que ce soit "getFoo()" alors super. Pensez à ce que vous voulez transmettre (est-ce vraiment le fichier ou une URL ou un chemin). Puisque vous avez dit que c'était un gros fichier, ce n'est peut-être pas une bonne idée d'instancier un gros objet en mémoire que vous allez réexécuter dans un format utile. Vous allez certainement payer le prix dans la collecte des ordures.

Ensuite, vous devriez penser à séparer la "découverte" du fichier - ou ouvrir le flux avec la logique d'analyse. Il y a beaucoup d'exceptions qui peuvent se produire en essayant simplement d'ouvrir le fichier - de ne pas être trouvé, de ne pas avoir d'autorisations, d'avoir trop de fichiers ouverts (ulimit).

Quand il s'agit de l'analyse, je suggère que vous envisagiez vraiment ce que vous voulez analyser et pour quoi. S'il s'agit d'un problème du monde réel, les «formats stables» sont toujours sujets à changement - en particulier lorsqu'il s'agit de tolérer des formats «invalides» - comme l'existence d'autres caractères non imprimables ou un EOF inattendu. Il y aura beaucoup de demandes pour gérer les choses avec élégance et aussi un besoin de comprendre les statistiques de ce qui a été analysé, ce qui a eu des erreurs et comment gérer cela.

Mes 2 cents.