Inleiding tot AutoFactory

1. Inleiding

In deze tutorial geven we een korte inleiding tot AutoFactory, van door Google.

Dit is een codegenerator op bronniveau die helpt bij het genereren van fabrieken.

2. Maven-instellingen

Voordat we beginnen, voegen we de volgende afhankelijkheid toe aan het pom.xml:

 com.google.auto.factory auto-factory 1.0-beta5 

De laatste versie vind je hier.

3. Snelstart

Laten we nu snel kijken naar wat AutoFactory kan een simple doen en creëren Telefoon klasse.

Dus als we de Telefoon les met @AutoFactory en zijn constructorparameter met @Geleverd, we krijgen:

@AutoFactory openbare klasse Telefoon {privé laatste camera camera; private finale String otherParts; PhoneAssembler (@Provided Camera camera, String otherParts) {this.camera = camera; this.otherParts = otherParts; } // ...}

We hebben slechts twee annotaties gebruikt: @AutoFactory en @Geleverd. Als we een fabriek nodig hebben die voor onze klas is gegenereerd, kunnen we deze annoteren met @AutoFactory, terwijl @Geleverd is van toepassing op constructorparameters van deze klasse, en het betekent dat de geannoteerde parameter moet worden geleverd door een geïnjecteerd Aanbieder.

In het bovenstaande fragment verwachten we de Camera te verstrekken door een cameraproducent en AutoFactory helpt bij het genereren van de volgende code:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") openbare laatste klas PhoneFactory {privé laatste Provider cameraProvider; @Inject PhoneAssemblerFactory (provider cameraProvider) {this.cameraProvider = checkNotNull (cameraProvider, 1); } PhoneAssembler create (String otherParts) {retourneer nieuwe PhoneAssembler (checkNotNull (cameraProvider.get (), 1), checkNotNull (otherParts, 2)); } // ...}

Nu hebben we een PhoneFactory automatisch gegenereerd door AutoFactory tijdens het compileren, en we kunnen het gebruiken om telefooninstanties te produceren:

PhoneFactory phoneFactory = nieuwe PhoneFactory (() -> nieuwe camera ("Unknown", "XXX")); Phone simplePhone = phoneFactory.create ("andere onderdelen");

De @AutoFactory annotatie kan ook worden toegepast op constructeurs:

openbare klasse ClassicPhone {privé final String-toetsenblok; privé finale String beltoon; private String otherParts; @AutoFactory openbare ClassicPhone (@Provided String-toetsenblok, @Provided String-beltoon) {this.dialpad = dialpad; this.ringer = beltoon; } @AutoFactory openbare ClassicPhone (String otherParts) {this ("defaultDialPad", "defaultRinger"); this.otherParts = otherParts; } // ...}

In het bovenstaande fragment hebben we toegepast @AutoFactory aan beide constructeurs. AutoFactory genereert eenvoudig twee aanmaakmethoden voor ons:

@Generated (value = "com.google.auto.factory.processor.AutoFactoryProcessor") openbare laatste klasse ClassicPhoneFactory {privé laatste Provider java_lang_StringProvider; @Inject openbare ClassicPhoneFactory (Provider java_lang_StringProvider) {this.java_lang_StringProvider = checkNotNull (java_lang_StringProvider, 1); } openbare ClassicPhone create () {retourneer nieuwe ClassicPhone (checkNotNull (java_lang_StringProvider.get (), 1), checkNotNull (java_lang_StringProvider.get (), 2)); } openbare ClassicPhone create (String otherParts) {retourneer nieuwe ClassicPhone (checkNotNull (otherParts, 1)); } // ...}

AutoFactory ondersteunt ook parameters die zijn geannoteerd met @Geleverd, maar alleen voor JSR-330-annotaties.

Als we bijvoorbeeld de cameraProvider om "Sony" te zijn, kunnen we de Telefoon klas naar:

@AutoFactory openbare klasse Telefoon {PhoneAssembler (@Provided @Named ("Sony") Cameracamera, String otherParts) {this.camera = camera; this.otherParts = otherParts; } // ...}

AutoFactory behoudt het @Genaamd@Kwalificatie zodat we er bijvoorbeeld gebruik van kunnen maken bij het gebruik van Dependency Injection frameworks:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") openbare laatste klas PhoneFactory {privé laatste Provider cameraProvider; @Inject PhoneAssemblerFactory (@Named ("Sony") Provider cameraProvider) {this.cameraProvider = checkNotNull (cameraProvider, 1); } // ...}

4. Aangepaste codegeneratie

Er zijn verschillende attributen die we kunnen gebruiken met de @AutoFactory annotatie om de gegenereerde code aan te passen.

4.1. Aangepaste klassenaam

De naam van de gegenereerde fabrieksklasse kan worden ingesteld met naam van de klasse:

@AutoFactory (className = "SamsungFactory") openbare klasse SmartPhone {// ...}

Met de bovenstaande configuratie maken we een klasse met de naam SamsungFactory:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") openbare laatste klasse SamsungFactory {// ...}

4.2. niet definitief Fabrieken

Merk op dat de gegenereerde fabrieksklasse standaard als definitief is gemarkeerd, dus we kunnen dit gedrag wijzigen door de allowSubclasses toe te schrijven aan vals:

@AutoFactory (className = "SamsungFactory", allowSubclasses = true) openbare klasse SmartPhone {// ...}

Nu hebben we:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") openbare klasse SamsungFactory {// ...}

4.3. Meer mogelijkheden

Bovendien kunnen we een lijst met interfaces specificeren voor de gegenereerde fabriek om te implementeren met behulp van de "implementatie ”parameter.

Hier hebben we de SamsungFactory om smartphones met aanpasbare opslag te produceren:

openbare interface CustomStorage {SmartPhone customROMInGB (int romSize); }

Merk op dat methoden in de interface instanties van de basisklasse moeten retourneren SmartPhone.

Om vervolgens een fabrieksklasse te genereren met de bovenstaande interface geïmplementeerd, AutoFactory vereist relevante constructeurs in de basisklasse:

@AutoFactory (className = "SamsungFactory", allowSubclasses = true, implementatie = CustomStorage.class) openbare klasse SmartPhone {openbare SmartPhone (int romSize) {// ...} // ...}

Dus, AutoFactory genereert de volgende code:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") openbare klasse SamsungFactory implementeert CustomStorage {// ... openbare SmartPhone create (int romSize) {retourneer nieuwe SmartPhone (romSize); } @Override openbare SmartPhone customROMInGB (int romSize) {return create (romSize); }}

4.4. Fabrieken met uitbreidingen

Sinds AutoFactory interface-implementaties kan genereren, het is normaal om te verwachten dat het ook klassen kan uitbreiden en dit is inderdaad mogelijk:

openbare abstracte klasse AbstractFactory {abstract CustomPhone newInstance (String brand); } @AutoFactory (extending = AbstractFactory.class) public class CustomPhone {private final String brand; openbare CustomPhone (String brand) {this.brand = brand; }}

Hier hebben we het AbstractFactory klasse gebruiken uitbreiden. We zouden ook moeten merk op dat elke abstracte methode in de basis abstracte klasse (AbstractFactory) moet een overeenkomstige constructor hebben in de klasse beton (CustomPhone).

Ten slotte kunnen we de volgende gegenereerde code zien:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") public final class CustomPhoneFactory breidt AbstractFactory uit {@Inject public CustomPhoneFactory () {} public CustomPhone create (String brand) {retourneer nieuwe CustomPhone (checkNotNull (brand, 1) ); } @Override public CustomPhone newInstance (String brand) {return create (brand); } // ...}

Dat kunnen we zien AutoFactory is slim genoeg om gebruik te maken van de constructor om de corresponderende abstracte methode te implementeren - geweldige functies zoals deze in AutoFactory zal ons zeker veel tijd en code besparen.

5. AutoFactory Met Guice

Zoals we eerder in dit artikel hebben vermeld, AutoFactory ondersteunt JSR-330-annotaties, zodat we het bestaande afhankelijkheidsinjectieframework ermee kunnen integreren.

Laten we eerst toevoegen Guice naar de pom.xml:

 com.google.inject guice 4.2.0 

De nieuwste versie van Guice vind je hier.

Nu zullen we demonstreren hoe goed AutoFactory integreert met Guice.

Aangezien we verwachten dat "Sony" de cameraprovider is, moeten we een SonyCameraProvider naar PhoneFactory‘S constructor:

@Generated ("com.google.auto.factory.processor.AutoFactoryProcessor") openbare laatste klas PhoneFactory {privé laatste Provider cameraProvider; @Inject openbare PhoneFactory (@Named ("Sony") Provider cameraProvider) {this.cameraProvider = checkNotNull (cameraProvider, 1); } // ...}

Ten slotte maken we de binding in een Guice module:

openbare klasse SonyCameraModule breidt AbstractModule uit {privé statisch int SONY_CAMERA_SERIAL = 1; @Named ("Sony") @Provides Camera cameraProvider () {retourneer nieuwe camera ("Sony", String.format ("% 03d", SONY_CAMERA_SERIAL ++)); }}

En we hebben de cameraprovider ingesteld met geannoteerd @Named ("Sony") in SonyCameraModule om te matchen PhoneFactory'S constructorparameter.

Nu kunnen we dat zien Guice beheert afhankelijkheidsinjectie voor onze gegenereerde fabriek:

Injector injector = Guice.createInjector (nieuwe SonyCameraModule ()); PhoneFactory injectedFactory = injector.getInstance (PhoneFactory.class); Telefoon xperia = injectedFactory.create ("Xperia");

6. Onder de motorkap

Alle annotaties geleverd door AutoFactory worden verwerkt in de compilatiefase, zoals we in het artikel in detail hebben uitgelegd: hoe de annotatieverwerking op bronniveau werkt.

7. Conclusie

In dit artikel hebben we uitgelegd hoe u AutoFactory kunt gebruiken en hoe u het kunt integreren Guice - schrijvende fabrieken kunnen repetitief en foutgevoelig zijn - tools voor het genereren van code zoals AutoFactory en AutoWaarde kan ons veel tijd besparen en ons bevrijden van subtiele bugs.

Zoals altijd is de volledige implementatie van de codevoorbeelden te vinden op Github.