Upload
others
View
5
Download
0
Embed Size (px)
Citation preview
The Flip Side of Dependency InjectionArne Blankerts | PHP UG Hamburg | March, 11th 2014
» Working with PHP for over a decade(Security paranoid ;) and System Architect)
» Author of phpab and phpDox
» Co-Founder and Consultantwith thePHP.cc
Arne Blankerts
The Problem1 <?php23 classclass Sample {45 privateprivate $backend$backend;6 privateprivate $service$service;78 public functionpublic function __construct() {9 $config$config = newnew Config('some.ini');
10 $this$this->backend = newnew Backend($config$config);11 $this$this->service = newnew SuperService($config$config, 'Sample');12 }1314 }
Solution - Attempt 11 <?php23 classclass Sample {45 privateprivate $backend$backend;6 privateprivate $service$service;78 public functionpublic function __construct() {9 $config$config = Config::getInstance();
10 $this$this->backend = newnew Backend($config$config);11 $this$this->service = newnew SuperService($config$config), 'Sample');12 }1314 }
Singleton?
Singleton? Really?
Solution - Attempt 21 <?php23 classclass Sample {45 privateprivate $backend$backend;6 privateprivate $service$service;78 public functionpublic function __construct(Config $config$config) {9 $this$this->backend = newnew Backend($config$config);
10 $this$this->service = newnew SuperService($config$config), 'Sample');11 }1213 }
The phonebookapproach
Solution - Attempt 31 <?php23 classclass Sample {45 privateprivate $backend$backend;6 privateprivate $service$service;78 public functionpublic function __construct(Config $config$config) {9 $this$this->backend = newnew Backend(
10 $config$config->get('backend.credentials')11 );12 $this$this->service = newnew SuperService(13 $config$config->get('service.hostname')14 );15 }1617 }
Almost there ...
Solution - Dependency Injection?1 <?php23 classclass Sample {45 privateprivate $backend$backend;6 privateprivate $service$service;78 public functionpublic function __construct(Backend $backend$backend, Service $service$service) {9 $this$this->backend = $backend$backend;
10 $this$this->service = $service$service;11 }1213 }
Solution - Dependency Injection!1 <?php23 classclass Sample {45 privateprivate $backend$backend;6 privateprivate $service$service;78 public functionpublic function __construct(9 BackendInterface $backend$backend,
10 ServiceInterface $service$service11 ) {12 $this$this->backend = $backend$backend;13 $this$this->service = $service$service;14 }1516 }
Many dependencies1 <?php23 classclass Sample {45 privateprivate $serviceA$serviceA;6 privateprivate $serviceB$serviceB;7 privateprivate $serviceC$serviceC;8 privateprivate $serviceD$serviceD;9 privateprivate $serviceE$serviceE;
1011 public functionpublic function __construct(12 ServiceAInterface $serviceA$serviceA, ServiceBInterface $serviceB$serviceB,13 ServiceCInterface $serviceC$serviceC, ServiceDInterface $serviceD$serviceD,14 ServiceEInterface $serviceE$serviceE15 ) {16 // [...]17 }1819 }
All-in-one?
Many Dependencies - A Solution?1 <?php23 classclass Sample {45 privateprivate $serviceA$serviceA;6 privateprivate $serviceB$serviceB;7 privateprivate $serviceC$serviceC;8 privateprivate $serviceD$serviceD;9 privateprivate $serviceE$serviceE;
1011 public functionpublic function __construct(Container $container$container) {12 $this$this->serviceA = $container$container->get('serviceA');13 // [...]14 }1516 }
Hidden Dependencies
Back At Square One
Magic?1 <?php23 classclass Sample {45 /**6 * @var ServiceA7 * @Inject8 */9 privateprivate $serviceA$serviceA;
1011 }
Advanced Magic?1 <?php23 classclass Sample {45 /**6 * @var ServiceA7 * @Inject("service.SuperServiceA")8 */9 privateprivate $serviceA$serviceA;
1011 }
Advanced advanced Magic?1 <?php23 classclass Sample {45 /**6 * @var ServiceA7 * @Inject("service.SuperServiceA", Instance="ThatInstance")8 */9 privateprivate $serviceA$serviceA;
1011 }
Extenral Configuration?1 services:2 serviceA:3 classclass: ServiceAImplementor4 serviceB:5 classclass: ServiceBImplementor6 serviceC:7 classclass: ServiceCImplementor
Still At Square One ...
What do we need?
Requirements• All Dependencies must be in code• Seperate Object creation from usage• Ability to choose actual implementation on runtime
Requirements• All Dependencies must be in code
• No hidden Dependencies• No external configuration• No framework magic based on annotations
• Seperate Object Creation from Usage• Ability to choose actual implementation on runtime
Requirements• All Dependencies must be in code• Seperate Object Creation from Usage• Ability to choose actual implementation on runtime
Requirements• All Dependencies must be in code• Seperate Object Creation from Usage• Ability to choose actual Implementation on Runtime
Requirements• All Dependencies must be in code• Seperate Object Creation from Usage• Ability to choose actual Implementation on Runtime
• Run consistent A/B-Tests• Degrade gracefully• Customized execution
How to do that in plain OOP?
Step 1
Factory
1 <?php23 classclass Factory {45 privateprivate $config$config;67 public functionpublic function __construct(Config $config$config) {8 $this$this->config = $config$config;9 }
1011 /**12 * @return ServiceA13 */14 public functionpublic function getServiceA() {15 return newreturn new ServiceA($this$this->getDBConnection());16 }1718 /**19 * @return DBConnection20 */21 private functionprivate function getDBConnection() {22 return newreturn new DBConnection($this$this->config->getDSN());23 }2425 }
Avoid injecting factories!
But what about runtimecomposing?
Locators!
1 <?php23 classclass FooLocator {45 /** @var Factory */6 privateprivate $factory$factory;78 public functionpublic function __construct(Factory $factory$factory) {9 $this$this->factory = $factory$factory;
10 }1112 /**13 * @return FooInterface14 */15 public functionpublic function get($type$type) {16 switchswitch ($type$type) {17 casecase 'a': returnreturn $this$this->factory->getServiceA();18 casecase 'b': returnreturn $this$this->factory->getServiceB();19 defaultdefault: throw newthrow new RuntimeException("Type '$type' not specified");20 }2122 }
That's it?
What happend to A/BTesting?
https://github.com/theseer/Factory
Done?
Done.
talks.thePHP.cc
@thePHPcc
sharing experience