Java: Inner classes accessing each other's private variables - good practice for encapsulating external API? -


this bit of design question involving inner classes in java (java 8). of example code below text

as example, let's have machinery involves pumping fuel oil geyser sort of burner, can control using external api called oilapi.

i have controller class doing work , decides burner needs oil geyser, don't want logic of using api's classes geyser , burner leak out controller (also since api still undergo changes on time).

now, encapsulate it, create class called fuelfacility contains of logic of oilapi.

the thing is, i've put classes pump , engine inner classes inside of fuelfacility.

first of all, syntax of being able go pump.activate() instead of fuelfacility.activatepump(...) or whatever.

further in order connect geyser , burner in oil api, need both geyser , burner objects, don't want expose them externally, in order have sort of "connect pump , engine" method, have allow either pump access engine's burner variable, engine access pump's geyser +variable, or fuelfacility access both these variables. in example below, have engine.connecttopump(pump) method, way works in actual code.

my teammates thought bit strange; said accessing private variables across classes breaks encapsulation, , programmer looking @ code "outside" (i.e., point of view of working in controller class) assume once have obtained engine , pump objects, no longer depend on e.g. oilapi object original fuelfacility using (although should remain final, i've done below) nor on each other.

now, since then, i've managed change minds bit - way of doing things they're not used to, it's not bad practice.

however, busy altering other code work in similar fashion this, , want make sure before continue, doing practice? there better way of doing things? advice appreciated!

code:

oil api (not under control):

public class oilapi {     private final pipes pipes = new pipes();      public static class geyser {}     public static class burner {}      public static class pipes {         public void createconnectionbetweengeyserandburner(geyser g, burner b) {             // connects geyser , burner         }        }      public geyser getgeyserwithid(string id) {         // retrieves specific instance         return new geyser();     }      public burner getburnerwithid(string id) {         // retrieves specific instance         return new burner();     }      public void activategeyser(geyser g) {         // stuff     }      public void activateburner(burner b) {         // stuff     }             public void createconnectionbetweengeyserandburner(geyser g, burner b) {         pipes.createconnectionbetweengeyserandburner(g,b);     } } 

fuel facility (class created encapsulate oil api):

public class fuelfacility {      private final oilapi oil;      fuelfacility(oilapi oil) {         this.oil = oil;     }      public pump getpumpforid(string id) {         oilapi.geyser geyser = oil.getgeyserwithid(id);         return new pump(geyser);     }      public engine getengineforid(string id) {         oilapi.burner burner = oil.getburnerwithid(id);         return new engine(burner);     }      public class pump {         private final oilapi.geyser geyser;         private pump(oilapi.geyser geyser) {             this.geyser = geyser;         }          public void activate() {             oil.activategeyser(geyser);         }     }      public class engine {         private final oilapi.burner burner;         private engine(oilapi.burner burner) {             this.burner = burner;         }          public void connecttopump(pump pump) {             oil.createconnectionbetweengeyserandburner(pump.geyser, burner);         }             public void activate() {             oil.activateburner(burner);         }     } } 

controller (owned me, , sits inside of our codebase):

public class controller {      public static void main(string[] args) {         // these database         string engineid = "engineid";         string pumpid = "pumpid";          oilapi oil = new oilapi();          fuelfacility facility = new fuelfacility(oil);         fuelfacility.engine engine = facility.getengineforid(engineid);         fuelfacility.pump pump = facility.getpumpforid(pumpid);         engine.connecttopump(pump);     } } 

having inner classes access each others' private fields isn't necessary bad in itself. seems main goal protect controller changes oilapi. in design, fuelfacility, pump, , engine close oilapi, geyser, , burner i'm not sure protect controller much. fuelfacility should designed more controller needs oilapi does. in example, don't call activate on pump or engine i'm assuming you'd want that. first, i'd start declaring interfaces:

public interface pumpengineconnection {   public void activate(); }  public interface fuelfacility {   public pumpengineconnection connect(string pumpid, string engineid); } 

controller works through these interfaces , not know implementations uses. can make oilapifuelfacility implementation of fuelfacility. pumpengineconnection implementation returns 1 designed work oilapifuelfacility. inner class:

public class oilapifuelfacility implements fuelfacility {   private final oilapi oil;   public oilapifuelfacility(oilapi oil){ this.oil = oil; }    @override   public pumpengineconnection connect(string pumpid, string engineid){      geyser geyser = oil.getgeyserwithid(pumpid);      burner burner = oil.getburnerwithid(engineid);      oil.createconnectionbetweengeyserandburner(geyser, burner);      return this.new geyserburnerconnection(geyser, burner);   }    private class geyserburnerconnection implements pumpengineconnection {      private final geyser geyser;      private final burner burner;       private geyserburnerconnection(geyser geyser, burner burner){        this.geyser = geyser;        this.burner = burner;      }       @override      public void activate() {         oilapifuelfacility.this.oil.activategeyser(this.geyser);         oilapifuelfacility.this.oil.activateburner(this.burner);      }   } } 

each geyserburnerconnection implicitly gets reference oilapifuelfacility created it. that's reasonable because makes sense use pumpengineconnection fuelfacility created it. likewise, it's reasonable geyserburnerconnection reference oil member oilapifuelfacility.

that said, may make more sense geyserburnerconnection package-private class in same package oilapifuelfacility. may different versions of oilapi can use same geyserburnerconnection class.

finally, controller this:

import com.example.fuelfacility.fuelfacility; import com.example.fuelfacility.pumpengineconnection; public controller {   private final fuelfacility fuelfacility;    public controller(fuelfacility fuelfacility){     this.fuelfacility = fuelfacility;   }    public void example(){      string pumpid = "pumpid";      string engineid = "engineid";       pumpengineconnection connection = fuelfacility.connect("pumpid", "engineid");      connection.activate();   } } 

note it's ignorant of implementation of fuelfacility , pumpengineconnection uses. in practice, we'd pass in oilapifuelfacility dependency injection framework or external main class.

i realize example simplified need do. nonetheless, should think in terms of controller needs , not oilapi does.

finally, should note agree colleagues' concerns design. consider snippet:

oilapi oil1 = new oilapi(); oilapi oil2 = new oilapi(); fuelfacility fuel1 = new fuelfacility(oil1); fuelfacility fuel2 = new fuelfacility(oil2); engine engine = fuel1.getengineforid("engineid"); pump pump = fuel2.getpumpforid("pumpid"); engine.connecttopump(pump); 

what happens? oil1 used connect pump retrieved through oil2. depending on internals of oilapi problem.


Comments

Popular posts from this blog

python - Selenium remoteWebDriver (& SauceLabs) Firefox moseMoveTo action exception -

html - How to custom Bootstrap grid height? -

transpose - Maple isnt executing function but prints function term -