Algemene interface

Stel dat ik een interface wilde definiëren die een oproep naar een externe service vertegenwoordigt. Nu retourneert de aanroep naar de externe service over het algemeen iets, maar kan ook invoerparameters bevatten. Stel dat een implementatieklasse doorgaans slechts één servicemethode implementeert. Gezien de bovenstaande informatie, is het volgende een slecht ontwerp (het voelt niet helemaal goed):

public interface IExecutesService<A,B>
{
    public A executeService();
    public A executeService(B inputParameter);
}

Laten we zeggen dat ik deze interface implementeer met een klasse die een externe service uitvoert met een invoerparameter:

public class ServiceA implements IExecutesService<String,String>
{
  public String executeService()
  {
    //This service call should not be executed by this class
    throw new IllegalStateException("This method should not be called for this class...blabla");
  }
  public String executeService(String inputParameter)
  {
    //execute some service
  }

Ik heb twee vragen over het bovenstaande:

  1. Is het gebruik van een generieke interface (IExecutesService<A,B>) goed in het geval dat u subklassen wilt aanbieden die verschillende invoerparameters en retourtypen voor de interfacemethoden vereisen?
  2. Hoe kan ik het bovenstaande beter doen? D.w.z. Ik wil mijn service uitvoeren onder een gemeenschappelijke interface (IExecutesService); Een uitvoeringsklasse implementeert echter meestal een van de methoden, en het gebruik van een IllegalStateException voelt echt lelijk. Ook de parameter B-type in IExecutesService<A,B>zal overbodig zijn voor een uitvoeringsklasse die een service oproept zonder invoerparameters. Het lijkt ook overkill twee afzonderlijke interfaces voor de twee verschillende servicecala.

Antwoord 1, Autoriteit 100%

Hier is een suggestie:

public interface Service<T,U> {
    T executeService(U... args);
}
public class MyService implements Service<String, Integer> {
    @Override
    public String executeService(Integer... args) {
        // do stuff
        return null;
    }
}

Vanwege het wissen van het type zal elke klasse alleen een van deze implementeren. Dit elimineert tenminste de redundante methode.

Het is geen onredelijke interface die u voorstelt, maar ik ben niet 100% zeker van welke waarde het voegt ook toe. Misschien wilt u gewoon de standaard Callableinterface. Het ondersteunt geen argumenten, maar dat deel van de interface heeft de minste waarde (IMHO).


Antwoord 2, Autoriteit 24%

Hier is een andere suggestie:

public interface Service<T> {
   T execute();
}

Met behulp van deze eenvoudige interface kunt u argumenten via constructor verstrekken in de Classes Concrete Services:

public class FooService implements Service<String> {
    private final String input1;
    private final int input2;
    public FooService(String input1, int input2) {
       this.input1 = input1;
       this.input2 = input2;
    }
    @Override
    public String execute() {
        return String.format("'%s%d'", input1, input2);
    }
}

Antwoord 3, autoriteit 11%

Ik zou bij twee verschillende interfaces blijven.

U zei dat ‘Ik wil mijn service-uitvoerders onder een gemeenschappelijke interface groeperen… Het lijkt ook overdreven om twee afzonderlijke interfaces te maken voor de twee verschillende serviceaanroepen… Een klasse zal slechts één van deze interfaces implementeren ‘

Het is niet duidelijk wat dan de reden is om een enkele interface te hebben. Als je het als markering wilt gebruiken, kun je in plaats daarvan annotaties gebruiken.

Een ander punt is dat het mogelijk is dat uw vereisten veranderen en methode(s) met een andere handtekening op de interface verschijnen. Natuurlijk is het dan mogelijk om Adapter-patroon te gebruiken, maar het zou nogal vreemd zijn om te zien dat die bepaalde klasse een interface implementeert met, laten we zeggen, drie methoden waarbij twee van hen UnsupportedOperationException gebruiken. Het is mogelijk dat de vierde methode verschijnt enz.


Antwoord 4, autoriteit 5%

Als antwoord dat strikt in overeenstemming is met uw vraag, steun ik het voorstel van cleytus.


Je zou ook een markeringsinterface kunnen gebruiken (zonder methode), zeg DistantCall, met verschillende subinterfaces die de precieze handtekeningen hebben die je wilt.

  • De algemene interface zou dienen om ze allemaal te markeren, voor het geval je een of andere generieke code voor ze allemaal wilt schrijven.
  • Het aantal specifieke interfaces kan worden verminderd door de generieke handtekening van cleytus te gebruiken.

Voorbeelden van ‘herbruikbare’ interfaces:

   public interface DistantCall {
    }
    public interface TUDistantCall<T,U> extends DistantCall {
      T execute(U... us);
    }
    public interface UDistantCall<U> extends DistantCall {
      void execute(U... us);
    }
    public interface TDistantCall<T> extends DistantCall {
      T execute();
    }
    public interface TUVDistantCall<T, U, V> extends DistantCall {
      T execute(U u, V... vs);
    }
    ....

Bijgewerkt In reactie op opmerking

Ik dacht niet aan een instantie van in de roeping. Ik dacht dat je roepcode wist wat het belde, en je moest alleen maar een aantal verre oproepen in een gemeenschappelijke interface verzamelen voor een aantal generieke code (, bijvoorbeeld het auditing van alle verre oproepen, om uitvoeringsredenen ).
In jouw vraag heb ik gezien geen vermelding dat de roepcode generiek is 🙁

Zo ja, stel ik aan dat u slechts één interface hebt, slechts één handtekening. Verscheidene zouden alleen meer complexiteit meenemen, voor niets.

U moet echter een aantal bredere vragen stellen:
Hoe u ervoor zult zorgen dat beller en callee correct communiceren?

Dat kan een follow-up zijn van deze vraag of een andere vraag …


Antwoord 5, Autoriteit 4%

Als ik het goed begrijp, wil je één klasse hebben meerdere van die interfaces met verschillende invoer / uitvoerparameters? Dit werkt niet in Java, omdat de generieke geneesmiddelen via Wissen worden geïmplementeerd.

Het probleem met de Java-generieken is dat de generieke geneesmiddelen in feite niets anders zijn dan compilermagie. Tijdens runtime houden de klassen geen informatie bij over de typen die worden gebruikt voor generieke dingen (klassetypeparameters, methodetypeparameters, interfacetypeparameters). Daarom kunt u, ook al zou u overbelasting van specifieke methoden hebben, deze niet binden aan meerdere interface-implementaties die alleen verschillen in hun generieke typeparameters.

Over het algemeen begrijp ik waarom je denkt dat deze code een geur heeft. Om u echter een betere oplossing te kunnen bieden, is het nodig om iets meer te weten over uw vereisten. Waarom wil je in de eerste plaats een generieke interface gebruiken?

Other episodes