Search code examples
javaabstract-factory

how to create abstract factory to instantiate objects in java


I would like to create an abstract factory. here is what I tried.

//abstract class Worker

public abstract class Worker {
    String phoneNumber;
    String firstName;
    String lastName;
    String workerType;
    String ifu;
    String imageParth;
    //....
  public String getWorkerType() {
        return workerType;
    }
}

// Electrician class which extends worker

package worker.domain.worker;

public class Electrician extends Worker{
    
    
    public Electrician() {}
    public Electrician(String phoneNumber, String firstName, String lastName, String ifu, String workerType,
            String imageParth) {
        super(phoneNumber, firstName, lastName, ifu,workerType, imageParth);
    }
    
    
    public String getWorkerType() {
        return "Electrician";
    }
    
}

//Mason class

package worker.domaine.worker;

public class Mason extends Worker{
    
    public Mason() {};

    public Mason(String phoneNumber, String firstName, String lastName, String ifu,String workerType,
            String imageParth) {
        super(phoneNumber, firstName, lastName, ifu, workerType, imageParth);
    }
    
    String getworkerType() {
        return "Mason";
    }
}

// interface WorkerAbstractFactory

package worker.domaine.worker;

public interface WorkerAbstractFactory {
    Worker createWorker(String typeWorker);
}

//

public class WorkerFactory implements WorkerAbstractFactory{

    @Override
    public Worker createWorker(String typeWorker) {
        Worker worker = null;
        if(worker != null) {
            switch (typeWorker) {
            case "Electrician":
                Electrician electrician =new Electrician();
                electrician = new Electrician (electrician.getPhoneNumber(), electrician.getFirstName(), electrician.getLastName(), electrician.getIfu(), electrician.getWorkerType(),electrician.getImageParth());
                
            case "Mason":
                Mason mason =new Mason();
                mason = new Mason (mason.getPhoneNumber(), mason.getFirstName(), mason.getLastName(), mason.getIfu(), mason.getworkerType(),mason.getImageParth());
                }}

//app class

public class WorkerFactoryProvider {
    
     public static WorkerAbstractFactory getWorkerFactory(String workerCategory) {
         //WorkerFactory workerFactory = new WorkerFactory();
         WorkerFactory workerFactory = new WorkerFactory();
         if (workerCategory != null) {
             switch (workerCategory) {
                case "Electrician":
                    Worker worker1 = workerFactory.createWorker("Electrician");
                    worker1.getWorkerType();
                    String a=worker1.getWorkerType();
                    System.out.println(a);  
                    
                case "Mason":
                    Worker worker2 = workerFactory.createWorker("Mason");
                    worker2.getWorkerType();
                    String b=worker2.getWorkerType();
                    System.out.println(b);               
             
             }
             
             
         }
         return null;
     }

do you think it could work like that? now, if I really want a concrete object, how could it be done? because I would like to write for example a method to calculate the pay of each worker according to type for example how could I use my abstract Factory in the method to return me each type.


Solution

  • You have a single class hierarchy of Worker types. To instantiate those you can just use a standalone factory class, you don't need an abstract factory here. For example this would be sufficient:

    public class WorkerFactory {            
        public Worker createWorker(String workerType) {
            switch (workerType) {
                case "Electrician": return new Electrician();                    
                case "Mason": return new Mason();
            }
        }
    }
    

    The abstract factory pattern is more elaborate, and allows injecting different concrete factories for related hierarchies of objects, so that the client doesn't need to be aware of the difference. For example you could have an abstract TransportationFactory:

    interface Transportation {
        void travelTo(String destination);
    }
    
    interface TransportationFactory {
        Transportation simple();
        Transportation luxurious();
    }
    

    And two concrete implementations (matching two different but similar class hierarchies):

    class WaterTransportationFactory {
       Transportation simple() {
           return new Kayak();
       }
       Transportation luxurious() {
           return new Yacht();
       }
    }
    

    And:

    class LandTransportationFactory {
       Transportation simple() {
           return new Bike();
       }
       Transportation luxurious() {
           return new RaceCar();
       }
    }
    

    The benefit of this pattern is that the client can be configured to use water or land transportation (or a new air transportation that is added later) without the need to undergo any changes:

    class Client {
    
        private TransportationFactory transportationFactory;
    
        public Client(TransportationFactory transportationFactory) {
            this.transportationFactory = transportationFactory;
        }
    
        public void travel(String destination) {
            transportationFactory.simple().travelTo(destination);
        }
    
        public void travelInStyle(String destination) {
            transportationFactory.luxurious().travelTo(destination);
        }
    
    }
    

    EDIT: You could change the simple/luxurious methods to match the style of your example with the getWorkerType method. I prefer to avoid the conditional logic if possible and let the created classes determine their availability themselves. This decouples even further, allowing hierarchy members to be added with minimal code changes:

    enum TransportationType {
        SIMPLE, LUXURIOUS
    }
    
    interface Transportation {
    
        void travelTo(String destination);
    
        // allow the class to specify its own type
        TransportationType getType();
    }
    
    // intermediate interface to distinguish Water from Land
    interface WaterTransportation extends Transportation {
    }
    
    class Kayak implements WaterTransportation {
    
        void travelTo(String destination) {
            // splash splash
        }
    
        TransportationType getType() {
            return TransportationType.SIMPLE;
        }
    }
    
    class WaterTransportationFactory {
    
       private WaterTransportation[] waterTransportations;
    
       // Inject all available beans implementing WaterTransportation
       // e.g. using Spring or some other dependency injection mechanism
       public WaterTransportationFactory(WaterTransportation[] waterTransportations) {
           this.waterTransportations = waterTransportations;
       }
    
       public Transportation create(TransportationType type) {
           for(WaterTransportation waterTransportation : waterTransportations) { 
               if (waterTransportation.getType() == type) {
                   // we are returning the same instance every time
                   // this could be ok for singleton beans
                   // but if you really need a fresh instance you could use builders (see below)
                   return waterTransportation;
               }
           }
           throw new IllegalArgumentException("No implementation for WaterTransportation type=" + type);
       }
    }
    

    An alternative with builders:

    KayakBuilder implements WaterTransportationBuilder {
        KayakBuilder name(String name) { ... };
        KayakBuilder weight(String weightInKg) { ... };
        KayakBuilder year(String yearBuilt) { ... };
        KayakBuilder speed(String averageSpeed) { ... };
        Kayak build() { return kayak; }
    }
    

    For more on Builders see this full exposition of the Builder pattern

    class WaterTransportationFactory {
    
       private WaterTransportationBuilder[] builders;
    
       // Inject all available WaterTransportationBuilders
       // e.g. using Spring or some other dependency injection mechanism
       public WaterTransportationFactory(WaterTransportationBuilder[] builders) {
           this.builders = builders;
       }
    
       // extra arguments can be passed to build the instance
       public Transportation create(TransportationType type, String name, int weightInKg, int yearBuilt, int averageSpeed) {
           for(WaterTransportationBuilder builder: builders) { 
               if (builder.getType() == type) {
                   return builder
                       .name(name)
                       .weight(weightInKg)
                       .year(yearBuilt)
                       .speed(averageSpeed)
                       .build();
               }
           }
           throw new IllegalArgumentException("No implementation for WaterTransportation type=" + type);
       }
    }