2009-12-31 22 views
5

Ho 5 o 6 classi che voglio seguire internamente la stessa struttura di base. In realtà la maggior parte di quelli che le classi dovrebbero seguire sono solo per l'uso della funzione stessa, quindi voglio davvero che questi metodi siano privati.Java: esiste un modo per forzare un'implementazione di metodi privati?

Esiste un modo per raggiungere questo obiettivo? So che le interfacce funzionerebbero bene ma non prenderanno membri privati ​​e non ti permetteranno di ridefinire l'ambito nel metodo implementato. C'è qualche soluzione alternativa per questo?

Grazie

+7

Quello che stai cercando di fare qui non ha alcun senso. Il punto dei metodi privati ​​è che non fanno parte della struttura pubblica della classe: per definizione, la classe dovrebbe comportarsi in modo identico indipendentemente dalla struttura dei metodi e dei campi privati. Se si desidera eseguire la componentizzazione della propria implementazione, è possibile farlo con metodi astratti o suddividere la classe in componenti, come suggerito da altri. Puoi chiarire il motivo per cui vuoi farlo? Se vuoi semplicemente semplificare facendo in modo che tutto il codice rispecchi una struttura simile dovrai farlo per convenzione, la lingua non può imporlo che –

+0

Sì, penso che tu abbia inchiodato sulla testa. Voglio che imponga una struttura a specchio. È troppo brutto che non possa essere fatto tramite costrutti linguistici. –

+0

Stai sbagliando. L'intero punto di incapsulamento è che non ti interessa come vengono implementate le cose, solo ciò che è il loro comportamento esterno. Le caratteristiche della lingua corrispondono alle buone pratiche ingegneristiche (soprattutto). –

risposta

16

Penso che il più vicino si può ottenere è usando una classe abstract con abstractprotected metodi:

abstract class A { 
    protected abstract void foo(); 
} 

class B extends A { 
    protected void foo() {} 
} 

per definire logica comune, è possibile chiamare il metodo protetto da un privato metodo nella classe super:

abstract class A { 
    private void bar() { 
     // do common stuff 
     foo(); 
    } 
    protected abstract void foo(); 
} 

in questo modo, è possibile consentire sottoclassi per riempire il privato comune template method con comportamenti specifici.

+0

Sì, è interessante. Sfortunatamente non ho una logica comune, vorrei solo forzare tutti gli oggetti ad usare gli stessi metodi interni. È un peccato che tu non possa forzarti su di loro. –

+2

Una classe astratta può anche avere metodi astratti di pacchetto-privato, quindi se è possibile forzare le sottoclassi nello stesso pacchetto, allora tali metodi possono essere quasi altrettanto privati. – Yishai

+0

So che è una risposta abbastanza vecchia, ma ho un dubbio. Come si invoca la barra dei metodi privata? Lo chiedo perché ho cose comuni da fare. –

6

creare una classe base astratta che delinea la struttura e il flusso comune. Specificare metodi astratti per i passaggi nel flusso che devono essere implementati dalle classi ereditanti.

+0

Ho svalutato questo senza rendermi conto che non ha risposto alla domanda. Come ho detto, stavo cercando di applicare metodi privati. Le classi astratte non lo fanno –

+0

Vedo cosa stavi cercando ora. Le classi astratte sono l'unico modo per imporre una struttura interna e un flusso comuni per l'ereditarietà delle classi, il compromesso è che non è possibile utilizzare metodi privati ​​per farlo. Non c'è modo di rafforzare le parti interne di un metodo privato tra le classi, temo. – Paolo

5

Hmm, le funzioni private non possono essere chiamate da altre classi, even by subclasses. Quindi qual è il punto di avere funzioni private con lo stesso nome in diverse classi?

+0

Se avevi diversi tipi di oggetti auto e volevi che tutti implementassero determinati metodi e li proteggessi da altri oggetti che hanno accesso, come faresti? –

+2

Ma se sono protetti da tutte le altre classi, perché sarebbe importante se esistessero o no? Non avrebbe alcun effetto sul funzionamento del programma come venivano chiamati o su come sono stati suddivisi. --- Quindi, ad esempio, se hai una funzione "shift (int gear)", potrebbe essere chiamata "changeGear (int gear)" in un'altra classe, e l'effetto sarebbe lo stesso. Oppure potresti avere una macchina con una sola marcia e nessuna funzione per cambiarla. Dal momento che le funzioni non possono essere chiamate da nessun'altra classe, non importa. - La protezione astratta funzionerebbe per quando è necessario chiamare le funzioni in sottoclassi. –

+0

Sì, questo è il punto. So che possono essere chiamati cose diverse ma non voglio che siano. Immagina di avere 10 classi di auto tutte usando nomi diversi internamente per la stessa funzione. Sì, puoi obiettare che non importa come lo implementa qualcun altro, il che è vero. Ma quando scrivi il codice per te stesso sarebbe utile che tutti usassero gli stessi nomi di metodo. Ciò vale ancor di più quando qualcuno nuovo si unisce al progetto, non sa nulla e finisce per modificare inavvertitamente le convenzioni di denominazione utilizzate in precedenza. –

1

Creare una classe base astratta con un metodo contrassegnato finale che descrive il flusso comune che include i metodi privati. Contrassegnarlo come finale significa che non può essere esteso da sottoclassi e quindi la logica aziendale viene applicata fino a quando il tuo codice chiamante lo utilizza. I punti di estensione possono essere creati contrassegnando i metodi come protetti. Ad esempio, diciamo di avere una classe che rappresenta un negozio al dettaglio.

private final void doTransaction() { 
    float amountDue; 

    // a protected or abstract method that extenders can override 
    Collection items = this.unloadShoppingCart(); 

    for (Object item : items) { 
     // another protected or abstract method 
     amountDue += this.getPrice(item); 
    } 

    // your private method 
    amountDue += this.getSalesTax(amountDue); 

} 
1

"throw MethodNotImplementedException();" potrebbe essere un costrutto utile

2

Creare una struttura "comune", con tutti i metodi privati ​​su di essi.

Quindi creare le 5 o 6 classi, ognuna con un campo di tipo "comune". Ovviamente non sarai in grado di chiamare i metodi privati ​​(ma tu dici che questi sono davvero interni alla classe) - dovrai pubblicizzare anche alcuni metodi pubblici per modificare lo stato, naturalmente.

public class common { 
    private method1() { ; } 
    private method2() { ; } 
    public other() { ; } 
... 
} 

public class myclass1 { 
    common commonMethods; 
} 

public class myclass2 { 
    common commonMethods; 
} 

o anche (assumere 'comune' è definito come sopra):

public class template { 
    common commonMethods; 
} 

public class myclass1 extends template { 
... 
} 

in modo da ottenere un campo 'commonMethods' (pacchetto-protetta) per il 'libero' su ciascuna delle 5 o 6 sottoclassi.

Dopo la discussione successiva su questo thread, sembra che l'autore non voglia realmente condividere la logica: solo le firme dei metodi essenzialmente, quindi questa risposta non si adatta a tale requisito.

3

Non è possibile applicarlo in fase di compilazione, ma è possibile scrivere un test unitario o un semplice programma per verificare l'esistenza dei metodi mediante la riflessione.

Suppongo che lo state facendo per rendere le classi coerenti per motivi estetici/di design. Se lo fai per qualche altro motivo dovresti davvero usare il modo protetto astratto che altri suggeriscono.

Ecco po 'di codice per iniziare su un tale test di strumento/unità (si dovrebbe migliorare i messaggi di errore per lo meno, e io consiglio veramente di test di unità piuttosto che quello che ho qui):

import java.lang.reflect.Method; 
import java.lang.reflect.Modifier; 

public class Main 
{ 
    public static void main(String[] args) 
    { 
     check(B.class, Modifier.PRIVATE, void.class, "doit", new Class<?>[] { int.class }); 
     check(C.class, Modifier.PRIVATE, void.class, "doit", new Class<?>[] { int.class }); 
    } 

    private static void check(final Class<?> clazz, 
           final int  modifiers, 
           final Class<?> returnType, 
           final String  name, 
           final Class<?>[] params) 
    { 
     try 
     { 
      final Method method; 

      method = clazz.getDeclaredMethod(name, params); 

      if(method.getModifiers() != modifiers) 
      { 
       System.out.println("modifiers do not match"); 
      } 

      if(method.getReturnType() != returnType) 
      { 
       System.out.println("return type does not match"); 
      } 
     } 
     catch(final NoSuchMethodException ex) 
     { 
      System.out.println("could not find method"); 
     } 
    } 
} 

interface A 
{ 
    void foo(); 
} 


class B 
    implements A 
{ 
    public void foo() 
    { 
     doit(0); 
    } 

    private void doit(final int x) 
    { 
    } 
} 

class C 
    implements A 
{ 
    public void foo() 
    { 
     doit(0); 
    } 

    private int doit(final int x) 
    { 
     return (5); 
    } 
} 
1

Se la protezione astratta non è veramente protetta abbastanza, mi chiedo quale sia la preoccupazione. In ogni caso, un'alternativa simile a monojohnny sarebbe quella di utilizzare il modello di strategia. Questo assicura che:

  • classi derivate devono definire il comportamento
  • classi derivate non possono accedere al comportamento dopo definisce in
  • casi non possono entrare comportamento di un altro

esempio, con scuse per prendere in prestito la metafora macchina nonostante l'assenza di braciole automotive:

public interface GearBoxStrategy { 
    public void changeGear(int newGear); 
} 

abstract public class Car { 
    private GearBoxStrategy gearBox; 
    public Car(GearBoxStrategy g) { 
     this.gearBox = g; 
    } 

    public void accelerate(double targetSpeed) { 
     int gear = getTargetGear(targetSpeed): 
     gearBox.shift(gear); 
    } 
} 

public class AutomaticTransmissionCar { 
    public AutomaticTransmissionCar() { 
     super(new AutomaticTransmissionGearBoxStrategy()); 
    } 
} 

public class ManualTransmissionCar { 
    public ManualTransmissionCar() { 
     super(new ManualTransmissionGearBoxStrategy()); 
    } 
} 
1

Date un'occhiata a XDepend, usa la reflection per creare un database basato sul tuo codice compilato.

http://www.xdepend.com

E 'rivolto ad architetti di software che desiderano avere la possibilità di controllare in modo rapido potenzialmente grandi librerie di codice compilato per i potenziali aree problematiche. Ha rapporti e visualizzazioni integrati per cose come le relazioni tra classi, complessità ciclomatica, accoppiamento ecc. Ecc.

Inoltre, include un linguaggio di query sql come "CQL" (per "linguaggio di query di codice"). Usando CQL puoi definire i tuoi report. Probabilmente dovresti essere in grado di usarlo per definire un rapporto per violazioni delle regole che descrivi. Inoltre, puoi incorporare le query CQL direttamente nel tuo codice usando le annotazioni.

Non l'ho esaminato, ma ho utilizzato il suo equivalente .NET "NDepend" ed è uno strumento molto interessante.

Naturalmente, è anche possibile scrivere il proprio strumento personalizzato che utilizza la riflessione per verificare le regole specifiche. XDepend potrebbe comunque valere la pena di essere guardato - dovrebbe essere molto più flessibile.

+0

Sembra interessante. E costoso, purtroppo. –

+0

Sì, è soprattutto se devi fornire a tutti gli sviluppatori una copia.È rivolto agli architetti di software, quindi l'aspettativa è che di solito hai solo un piccolo numero di copie. – Phil

1

È possibile rendere tutte le classi ereditate dalla stessa classe base?

Se è così, una cosa che si potrebbe prendere in considerazione sarebbe in fase di esecuzione in uso costruttore riflessione della classe base, per verificare che la sottoclasse sta seguendo le regole che descrivono, e lanciare un'eccezione se non riesce vostre regole di validazione.

L'ingenua implementazione di questo test ovviamente avrebbe problemi di prestazioni significativi, quindi dovresti essere abbastanza intelligente nel modo in cui implementa il test.

Per iniziare, il test deve essere eseguito una sola volta per tutte le istanze di un particolare sottotipo T. Pertanto, si dovrà memorizzare le informazioni di convalida da qualche parte. Un modo per farlo sarebbe quello di utilizzare una sorta di tabella di hash statica (globale) nella classe base digitata sul tipo di ogni sottotipo.

Dovresti anche eseguire una sorta di sincronizzazione thread-safe attorno a questa cache. Ciò di cui hai davvero bisogno per evitare questo è un successo di prestazioni per le letture. Quello che ho fatto in un caso simile prima era usare una combinazione del modello di blocco a doppio controllo e l'uso di un hashbile immutabile in modo tale da ottenere solo un colpo di performance per il blocco quando si tenta di scrivere sull'hashtable (cioè quando si crea il prima istanza di un particolare sottotipo T).

In realtà non sono esperto in Java, quello che descrivo, l'ho implementato in .NET, che è il motivo per cui non posso fornire un esempio di codice, ma tutti i concetti dovrebbero essere facilmente trasferibili a Java - tutto ciò che menzione è (AFAIK) disponibile su entrambe le piattaforme.

2

Mentre i metodi di interfaccia devono essere sempre pubblici, è possibile rendere privato il pacchetto dell'interfaccia e conservare tutte le implementazioni Car (ad esempio) nello stesso pacchetto.

package com.some.car.pkg; 

interface Car 
{ 
    public void gas(); 
    public void brake(); 
} 

Anche se i metodi sono pubblici, non importa in quanto al di fuori della com.some.car.pkg pacchetto, auto non è visibile. In questo modo, tutti i tuoi implementatori non sarebbero costretti ad estendere una classe astratta. Il fatto che tu voglia metodi comuni significhi veramente privato non è la vera soluzione, e IMHO, tu vuoi un'interfaccia, dal momento che sembra che nel tuo caso una classe astratta non sia del tutto corretta dato che non esiste una logica condivisa.

I miei 2 centesimi.

+0

Mi sembra un modo pulito di farmi le cose: e sembra soddisfare entrambi i requisiti: gli implementatori dovranno scrivere i metodi con le firme richieste dall'interfaccia in fase di progettazione e la struttura del pacchetto mantiene l'intera classe e tutti i suoi metodi lontani da altre classi. Anche ai suoni sembra che non ci sia una vera e propria logica condivisa tra le classi: quindi sono d'accordo con le classi astratte che non sono esattamente adatte. – monojohnny

0

Ecco un'idea: scrivere un parser di testo semplice per verificare l'esistenza dei metodi. Includilo come compito in Ant. Finché si insiste su una qualche forma di standard di codifica, alcuni semplici abbinamenti di testo dovrebbero farlo, cioè cercare semplicemente la firma formattata nei file di origine richiesti.

0

In un commento hai scritto "Sì, questo è il punto, so che possono essere chiamati cose diverse ma non voglio che siano".

Ora, qualcuno potrebbe semplicemente dire "questo è impossibile", ma come molte cose nella programmazione, non è in realtà impossibile, è solo un sacco di lavoro.

Se si desidera eseguire questa operazione, è possibile creare un numero personalizzato Java Annotation per la classe e quindi scrivere un Annotation processor e chiamare apt come parte del processo di compilazione.

Come ho detto molto lavoro, ma potrebbe essere utile se si desidera imparare come funzionano le annotazioni.


Scrivere annotazioni è in realtà piuttosto semplice. Funzionano come le normali lezioni. Ad esempio, se si desidera solo per contrassegnare una classe per qualche motivo è possibile creare un vuoto o marcatore annotazione come questo

public @interface Car { } 

Poi, nel tuo processore annotazione è possibile verificare che auto ha i metodi privati ​​giusti .

Ho scritto le mie annotazioni, ma le ho verificate in Runtime utilizzando l'API di reflection, piuttosto che al momento della compilazione. Sono in realtà piuttosto facili.

Problemi correlati