PHP Dependency Injection ile bir Controller sınıfından Besteleme

7 Cevap php

Nasıl olmalı PHP composing a Controller sınıfının sorunu çözmek için:

  • kolayca testable Bağımlılık Enjeksiyon kullanılarak,
  • shared objects bitiş programcı sağlamak
  • load new user libraries için bir yol sağlar

Look down, for controller instantiation with a Dependency injection framework


Sorun şu ki, türetilen Kontrolörleri (örn. çerçeve sağlar) programcı için ne isterse kaynakları kullanabilir olduğunu. Paylaşılan kaynaklar (DB, Kullanıcı, Depolama, Cache, Yapanlar), kullanıcı tanımlı sınıfları veya başka kitaplıklara bir birleşik erişim oluşturmak nasıl?

Elegant solution?

Orada benim sorunun birkaç olası çözüm vardır, ama ne biri şık olarak görünüyor

  • constructor tarafından paylaşılan tüm nesneleri geçirmek için çalışın? (10 bile tutucular ile kurucu yaratabilir)
  • Create getters, settters? (Şişirilmiş kodu) $controller->setApplication($app)
  • Uygula singletons on shared resources? User::getInstance() ya da Database::getInstance()
  • Dependency Injection container kontrolör içine nesne paylaşımı için bir tek olarak kullan?
  • Bir fabrika gibi bir global application singleton sağlar? (O DI ilke ve Demeter'in yasaya şiddetle karşı gider Hovewer bu bir çok php çerçeveler kullanılan görünüyor)

Ancak ben bu paradigma onlar sağlanan paylaşılan kaynaklara erişebilir olması gereken diğer programcılar (Controller sınıfı), için bir başlangıç ​​noktası için geçerlidir nasıl bilmiyorum, güçlü çift sınıfları oluşturarak :) vazgeçiliyor ve sürgün olduğunu anlamak MVC mimarisi. Ben küçük sınıflara controller sınıfı kesiliyor şekilde MVC pratik anlamı yok edeceğini inanıyorum.


Dependency Injection Framework

DI Çerçeve uygulanabilir bir seçim gibi görünüyor. Ancak sorun hala devam etmektedir. Kontrolörü gibi A sınıfı Uygulama tabakasında bulunur, ancak RequestHandler / Response katmanda değildir.

How should this layer instantiate the controller?

  • Bu tabakanın içine DI enjektörü geçmek?
  • Bir tek olarak DI Çerçeve?
  • Sadece bu katman için izole DI çerçeve yapılandırma koymak ve ayrı DI enjektör örneğini oluşturmak?

7 Cevap

Eğer bir çerçevesinin kendinizi geliştirmeye musunuz? Eğer değilse already existing frameworks ve mevcut çözümleri seçmek zorunda, çünkü soru, geçerli değildir. Bu durumda soru "Ben çerçevesi X birim test / bağımlılık enjeksiyon nasıl yaparım" gibi yeniden formüle edilmelidir.

Eğer kendi üzerinde bir çerçeve geliştirilmesi iseniz, mevcut olanları bu sorunu nasıl yaklaşım zaten ilk kontrol etmelisiniz. Ve siz de kendi gereksinimleri ayrıntılı, ve sonra sadece basit olası çözümü ile gitmek gerekir. Gereksinimleri olmadan, soru tamamen estetik ve tartışmacı.

Basit çözüm aksi takdirde burada mocks enjekte edebilir, çerçeve tarafından sağlanan varsayılan başlatılamadı kamu özelliklere sahip olmaktır imho. (Bu alıcılar / setters çözümüne eşittir, ancak söz konusu kabartmak olmadan. Her zaman alıcı ve ayarlayıcıları gerekmez.) İsteğe bağlı olarak, gerçekten ihtiyacınız varsa, (size önerilen) bir çağrıda bu başlatmak için bir kurucu sağlayabilir .

Singletons zarif bir çözüm vardır, ama yine de, bu durumda uygulanabilir, kendinize sormanız gerekir? Eğer uygulamada nesne aynı türde farklı örnekleri için varsa (yalnızca app yarısında bir sınıfı taklit etmek isterseniz örneğin), onunla devam edemez.

Tabii ki tüm seçenekleri var gerçekten harika. Sen alıcılar / setter, kurucular olabilir, ve başlatma ihmal edildiğinde, varsayılan bir tek fabrikadan alınır. Ama gerektiğinde değil çok fazla seçenek olması, programcı kongre, opsiyon ve desen kullanmak için hangi anlamaya vardır gibi rahatsız edici, müthiş değil. Ben kesinlikle sadece basit bir CRUD çalışan almak için tasarım kararlarının onlarca yapmak istemiyorum.

If you look at other frameworks you will see that there is no silver bullet. Often a single framework utilizes different techniques depending on the context. In controllers, DI is a really straightforward thing, look at CakePHP's $helpers, $components variables, which instruct to inject appropriate variables into the controller class. For the application itself a singleton is still a good thing, as there is always just a single application. Properties less often changed/mocked are injected utilizing public properties. In case of an MVC, subclassing is perfectly viable option as well: just as AppController, AppView, AppModel in CakePHP. They are inserted into the class hierarchy between the frameworks's and all your particular Controller, View and Model classes. This way you have a single point to declare globals for your main type of classes.

Java'da, çünkü dinamik sınıf yükleyiciler ve yansıma, seçim için bile çok fazla seçenek var. Paralel istekleri, paylaşılan nesneler ve işçi iş parçacığı, dağıtılmış uygulama sunucuları vb arasındaki devletler: Ama diğer taraftan, çok daha gereksinimlerini de destek var

Siz sadece size ilk etapta ne gerek biliyorsanız, sizin için doğru olanı soruya cevap olabilir. Ama aslında, neden yine sadece yeni bir çerçeve yazıyorsunuz?

Belki bu küçük DI kütüphaneye bakabilirsiniz. Çok basit ve kullanımı test edilebilir

http://gabordemooij.com/harbour/

Liman benim projem ama kaliteli bakım bir sicili var. Ben de yazdı: http://www.redbeanphp.com, sen Harbour bakımlı ve iyi belgelenmiş olacak emin olabilirsiniz. Yani endişelenmeyin. Genç ve henüz belgelenmemiş iken de, aynı zamanda son derece basit; sadece 3-4 sınıflar ve kod az 600 satır sanırım. Ben yaklaşık iki hafta içinde resmi olarak yayımlamayı planlıyoruz.

Peki refactoring?

Verilen bu seçeneklerden biri değildi, ama sen kod büyük ölçüde birleştiğinde sınıf devlet. Neden bu kez ve daha modüler, test edilebilir bileşenlere refactor çaba almaz?

Olarak anladığım kadarıyla, senin Uygulama sınıfı memuru olmalıdır. Eğer öyleyse, ben değil Uygulamanın bir örneğini geçmek denetleyici kurucu kullanmak istiyorsunuz, bu nedenle kontrolör, bunu çağırarak kim olduğunu bilmek istiyorum. Eğer kod CLI içinde çağrıldığında bağlı olarak farklı bir uygulama örneği var isterseniz daha sonra noktada, Application \ Http ve Uygulama \ CLI uygulamak ve her şeyi bakımı kolay olacak bir ApplicationInterface olabilir.

Ayrıca DI güzel bir uygulama almak için bazı fabrika deseni uygulamak olabilir. Örneğin, burada createThroughReflection metodunu kontrol: https://github.com/troelskn/bucket/blob/master/lib/bucket.inc.php

Bu mantıklı umuyoruz.

Regards, Nick

Ayrıca Uygulama ya da Router / Dispatcher'ın vermek hangi bir ControllerFatory kullanabilirsiniz

Eğer $ controllerFactory-> createController ($ isim) diyebiliriz sou;

Application denetleyicileri Fabrika olur oluşturmak için nasıl hiçbir fikrim yok olurdu. Eğer DI konteyner için kendi ControllerFactory enjekte ca beri size denetleyicisi bağlı istediğiniz tüm bağımlılıkları yönetebilirsiniz.

class ControllerFactory {
    public function __construct(EvenDispatcher $dispatcher,
                                Request $request,
                                ResponseFactory $responseFactory, 
                                ModelFactory $modelFactory, 
                                FormFactory $formFactory) {
        ...
    }

    public function createController($name = 'Default') {
        switch ($name) {
            case 'User':
              return new UserController($dispatcher, 
                                        $request, 
                                        $responseFactory->createResponse('Html'), 
                                        $modelFactory->createModel('User'),
                                        $formFactory->createForm('User'),...);

              break;
            case 'Ajax':
              return new AjaxController($dispatcher, 
                                        $request, 
                                        $responseFactory->createResponse('Json'), 
                                        $modelFactory->createModel('User'));
              break;
            default:
                 return new DefaultController($dispatcher, $request, $responseFactory->createResponse('Html'));
        }
    }

} 

So you just need to add this factory in your DI container and pass it to your Application. Whenever you need a new controller you add it to the factory and if new dependencies are required you give them to the factory through your DI container.

class App {
    public function __construct(Router $router,Request $request, ControllerFactory $cf, ... ) {
      ...
    }

    public function execute() {
        $controllerName = $this->router->getMatchedController();
        $actionName $this->router->getMatchedAction();

        $controller = $cf->createController($controllerName);

        if(is_callable($controller, $actionName)) {
            $response = $controller->$action(request);
            $response->send();     
        }
    }
}

Bu benim test değil, üretim kodu değil, ama bu uygulamadan denetleyicileri decouple nasıl. Benim denetleyicisi dönüş bir tepki var ve ben App tepkisini yürütmek çünkü bir kötü birleştirme burada olduğunu ama burada dikkat edin. Ama dediğim gibi bu sadece küçük bir örnek olduğunu söyledi.

Bu hangi gerçekten kötü ve bellek tüketen önyükleme anda tüm nesne grafiği yükleme sona erecek, çünkü, Modeller, Formlar, ve kendi ebeveynlerine Kontrolörleri için fabrikalar geçmek için genellikle iyi bir fikir değildir.

Ben bu cevabı zaten onaylandı biliyorum, ama bu konuda benim 2 sent

Konuda iyi bir makale var

http://miller.limethinking.co.uk/2011/07/07/dependency-injection-moving-from-basics-to-container/

Singletons Bağımlılık Enjeksiyon geçerli olduğunda kaşlarını çattı (ve ben bir Singleton gerekli olduğunu bir durumda bulmak için henüz) vardır.

Daha muhtemel denetleyicileri örnekleme kontrolü var, bu yüzden söz konusu $controller->setApplication($application) ile alabilirsiniz, ancak gerekirse bir uygulama dikeylikten için çok daha az zararlı statik yöntemleri ve değişkenleri (kullanabilirsiniz Singletons hariç); yani Controller::setApplication(), ve örnek yöntemlerle statik değişkenlere erişmek.

örneğin:

// defining the Application within the controller -- more than likely in the bootstrap
$application = new Application();
Controller::setApplication($application);

// somewhere within the Controller class definition
public function setContentType($contentType)
{
    self::$application->setContentType($contentType);
}

Ben statik ve örnek özelliklerini ve yöntemlerini ayıran (gerekli, ve hala sınıf tanımının üst gruplandırma özelliklerini) bir alışkanlık yaptık. Ben sınıfları hala oldukça kompakt kalır gibi bu, tekil olan daha az hantal olduğunu hissediyorum.