DI: Reworked dependency-factory implementation draft complete -- move into library headers
This is a complete makeover of our lib::Depend and lib::DependencyFactory templates. While retaining the basic idea, the configuration has been completely rewritten to favour configuration at the point where a service is provided rather, than at the point where a dependency is used. Note: we use differently named headers, so the entire Lumiera code base still uses the old implementation. Next step will be to switch the tests (which should be drop-in)
This commit is contained in:
parent
957e7ff54c
commit
83476b3ef1
5 changed files with 429 additions and 618 deletions
321
research/try.cpp
321
research/try.cpp
|
|
@ -46,18 +46,12 @@
|
||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
|
|
||||||
#include "lib/format-cout.hpp"
|
#include "lib/format-cout.hpp"
|
||||||
#include "lib/depend.hpp"
|
|
||||||
#include "lib/depend2.hpp"
|
#include "lib/depend2.hpp"
|
||||||
#include "lib/meta/util.hpp"
|
#include "lib/depend-inject.hpp"
|
||||||
//#include "lib/meta/util.hpp"
|
//#include "lib/meta/util.hpp"
|
||||||
#include "lib/test/test-helper.hpp"
|
#include "lib/test/test-helper.hpp"
|
||||||
#include "lib/util.hpp"
|
#include "lib/util.hpp"
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
#include <functional>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
|
|
||||||
#define SHOW_TYPE(_TY_) \
|
#define SHOW_TYPE(_TY_) \
|
||||||
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
|
cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <<endl;
|
||||||
|
|
@ -67,317 +61,8 @@ typedef unsigned int uint;
|
||||||
|
|
||||||
namespace error = lumiera::error;
|
namespace error = lumiera::error;
|
||||||
|
|
||||||
using lib::ClassLock;
|
using lib::Depend;
|
||||||
using lib::meta::enable_if;
|
using lib::DependInject;
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<typename TAR, typename SEL =void>
|
|
||||||
class InstanceHolder
|
|
||||||
: boost::noncopyable
|
|
||||||
{
|
|
||||||
std::unique_ptr<TAR> instance_;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
TAR*
|
|
||||||
buildInstance()
|
|
||||||
{
|
|
||||||
if (instance_)
|
|
||||||
throw error::Fatal("Attempt to double-create a singleton service. "
|
|
||||||
"Either the application logic, or the compiler "
|
|
||||||
"or runtime system is seriously broken"
|
|
||||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
|
|
||||||
// place new instance into embedded buffer
|
|
||||||
instance_.reset (new TAR{});
|
|
||||||
return instance_.get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ABS>
|
|
||||||
class InstanceHolder<ABS, enable_if<std::is_abstract<ABS>>>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ABS*
|
|
||||||
buildInstance()
|
|
||||||
{
|
|
||||||
throw error::Fatal("Attempt to create a singleton instance of an abstract class. "
|
|
||||||
"Application architecture or lifecycle is seriously broken.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}//(End)Implementation helper
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<class SRV>
|
|
||||||
class DependInject;
|
|
||||||
|
|
||||||
template<class SRV>
|
|
||||||
class Depend
|
|
||||||
{
|
|
||||||
using Factory = std::function<SRV*()>;
|
|
||||||
|
|
||||||
static SRV* instance;
|
|
||||||
static Factory factory;
|
|
||||||
|
|
||||||
static InstanceHolder<SRV> singleton;
|
|
||||||
|
|
||||||
friend class DependInject<SRV>;
|
|
||||||
public:
|
|
||||||
|
|
||||||
SRV&
|
|
||||||
operator() ()
|
|
||||||
{
|
|
||||||
if (!instance)
|
|
||||||
retrieveInstance();
|
|
||||||
ENSURE (instance);
|
|
||||||
return *instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void
|
|
||||||
retrieveInstance()
|
|
||||||
{
|
|
||||||
ClassLock<SRV> guard;
|
|
||||||
|
|
||||||
if (!instance)
|
|
||||||
{
|
|
||||||
if (!factory)
|
|
||||||
instance = singleton.buildInstance();
|
|
||||||
else
|
|
||||||
instance = factory();
|
|
||||||
factory = disabledFactory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SRV*
|
|
||||||
disabledFactory()
|
|
||||||
{
|
|
||||||
throw error::Fatal("Service not available at this point of the Application Lifecycle"
|
|
||||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class SRV>
|
|
||||||
SRV* Depend<SRV>::instance;
|
|
||||||
|
|
||||||
template<class SRV>
|
|
||||||
typename Depend<SRV>::Factory Depend<SRV>::factory;
|
|
||||||
|
|
||||||
template<class SRV>
|
|
||||||
InstanceHolder<SRV> Depend<SRV>::singleton;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////Configuration
|
|
||||||
|
|
||||||
using std::move;
|
|
||||||
|
|
||||||
|
|
||||||
template<class SRV>
|
|
||||||
struct DependInject
|
|
||||||
{
|
|
||||||
using Factory = typename Depend<SRV>::Factory;
|
|
||||||
|
|
||||||
|
|
||||||
/** configure dependency-injection for type SRV to build a subclass singleton
|
|
||||||
* @tparam SUB concrete subclass type to build on demand when invoking `Depend<SRV>`
|
|
||||||
* @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already
|
|
||||||
* been invoked at the point when calling this (re)configuration function.
|
|
||||||
*/
|
|
||||||
template<class SUB>
|
|
||||||
static void
|
|
||||||
useSingleton()
|
|
||||||
{
|
|
||||||
__assert_compatible<SUB>();
|
|
||||||
static InstanceHolder<SUB> singleton;
|
|
||||||
installFactory ([&]()
|
|
||||||
{
|
|
||||||
return singleton.buildInstance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration handle to expose a service implementation through the `Depend<SRV>` front-end.
|
|
||||||
* This noncopyable (but movable) handle shall be planted within the context operating the service
|
|
||||||
* to be exposed. It will immediately create (in RAII style) and manage a heap-allocated instance
|
|
||||||
* of the subclass `IMP` and expose a baseclass pointer to this specific instance through `Depend<SRV>`.
|
|
||||||
* Moreover, the implementation subclass can be accessed through this handle, which acts as smart-ptr.
|
|
||||||
* When the handle goes out of scope, the implementation instance is destroyed and the access through
|
|
||||||
* `Depend<SRV>` is closed and inhibited, to prevent on-demand creation of a baseclass `SRV` singleton.
|
|
||||||
* @tparam IMP concrete service implementation subclass to build, manage and expose.
|
|
||||||
* @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already
|
|
||||||
* been invoked at the point when calling this (re)configuration function.
|
|
||||||
*/
|
|
||||||
template<class IMP>
|
|
||||||
class ServiceInstance
|
|
||||||
{
|
|
||||||
std::unique_ptr<IMP> instance_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ServiceInstance()
|
|
||||||
: instance_(new IMP{})
|
|
||||||
{
|
|
||||||
__assert_compatible<IMP>();
|
|
||||||
activateServiceAccess (*instance_);
|
|
||||||
}
|
|
||||||
|
|
||||||
~ServiceInstance()
|
|
||||||
{
|
|
||||||
deactivateServiceAccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceInstance (ServiceInstance&&) = default;
|
|
||||||
ServiceInstance (ServiceInstance const&) = delete;
|
|
||||||
ServiceInstance& operator= (ServiceInstance&&) = delete;
|
|
||||||
|
|
||||||
explicit
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return bool(instance_);
|
|
||||||
}
|
|
||||||
|
|
||||||
IMP&
|
|
||||||
operator* () const
|
|
||||||
{
|
|
||||||
ENSURE (instance_);
|
|
||||||
return *instance_;
|
|
||||||
}
|
|
||||||
|
|
||||||
IMP*
|
|
||||||
operator-> () const
|
|
||||||
{
|
|
||||||
ENSURE (instance_);
|
|
||||||
return instance_.get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration handle for temporarily shadowing a dependency by a test mock instance.
|
|
||||||
* This noncopyable (but movable) handle shall be planted within the immediate test context.
|
|
||||||
* It immediately stashes away the existing state and configuration from `Depend<SRV>`, but
|
|
||||||
* waits for actual invocation of the `Depend<SRV>`-front-end to create a heap-allocated
|
|
||||||
* instance of the `MOC` subclass, which it manages and exposes like a smart-ptr.
|
|
||||||
* When the handle goes out of scope, the original state and configuration is restored
|
|
||||||
*/
|
|
||||||
template<class MOC>
|
|
||||||
class Local
|
|
||||||
{
|
|
||||||
std::unique_ptr<MOC> mock_;
|
|
||||||
|
|
||||||
SRV* origInstance_;
|
|
||||||
Factory origFactory_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Local()
|
|
||||||
{
|
|
||||||
__assert_compatible<MOC>();
|
|
||||||
temporarilyInstallAlternateFactory (origInstance_, origFactory_
|
|
||||||
,[this]()
|
|
||||||
{
|
|
||||||
mock_.reset(new MOC{});
|
|
||||||
return mock_.get();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
~Local()
|
|
||||||
{
|
|
||||||
restoreOriginalFactory (origInstance_, origFactory_);
|
|
||||||
}
|
|
||||||
|
|
||||||
Local (Local&&) = default;
|
|
||||||
Local (Local const&) = delete;
|
|
||||||
Local& operator= (Local&&) = delete;
|
|
||||||
|
|
||||||
explicit
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return bool(mock_);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOC&
|
|
||||||
operator* () const
|
|
||||||
{
|
|
||||||
ENSURE (mock_);
|
|
||||||
return *mock_;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOC*
|
|
||||||
operator-> () const
|
|
||||||
{
|
|
||||||
ENSURE (mock_);
|
|
||||||
return mock_.get();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected: /* ======= internal access-API for those configurations to manipulate Depend<SRV> ======= */
|
|
||||||
template<class IMP>
|
|
||||||
friend class ServiceInstance;
|
|
||||||
template<class MOC>
|
|
||||||
friend class Local;
|
|
||||||
|
|
||||||
|
|
||||||
template<class SUB>
|
|
||||||
static void
|
|
||||||
__assert_compatible()
|
|
||||||
{
|
|
||||||
static_assert (std::is_base_of<SRV,SUB>::value,
|
|
||||||
"Installed implementation class must be compatible to the interface.");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
installFactory (Factory&& otherFac)
|
|
||||||
{
|
|
||||||
ClassLock<SRV> guard;
|
|
||||||
if (Depend<SRV>::instance)
|
|
||||||
throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
|
|
||||||
"The previously installed factory (typically Singleton) was already used."
|
|
||||||
, error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
Depend<SRV>::factory = move (otherFac);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac)
|
|
||||||
{
|
|
||||||
ClassLock<SRV> guard;
|
|
||||||
stashFac = move(Depend<SRV>::factory);
|
|
||||||
stashInstance = Depend<SRV>::instance;
|
|
||||||
Depend<SRV>::factory = move(newFac);
|
|
||||||
Depend<SRV>::instance = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
restoreOriginalFactory (SRV*& stashInstance, Factory& stashFac)
|
|
||||||
{
|
|
||||||
ClassLock<SRV> guard;
|
|
||||||
Depend<SRV>::factory = move(stashFac);
|
|
||||||
Depend<SRV>::instance = stashInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
activateServiceAccess (SRV& newInstance)
|
|
||||||
{
|
|
||||||
ClassLock<SRV> guard;
|
|
||||||
if (Depend<SRV>::instance)
|
|
||||||
throw error::Logic("Attempt to activate an external service implementation, "
|
|
||||||
"but another instance has already been dependency-injected."
|
|
||||||
, error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
Depend<SRV>::instance = &newInstance;
|
|
||||||
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
deactivateServiceAccess()
|
|
||||||
{
|
|
||||||
ClassLock<SRV> guard;
|
|
||||||
Depend<SRV>::instance = nullptr;
|
|
||||||
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////Usage
|
///////////////////////////////////////////////////////Usage
|
||||||
|
|
|
||||||
278
src/lib/depend-inject.hpp
Normal file
278
src/lib/depend-inject.hpp
Normal file
|
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
DEPENDENCY-FACTORY.hpp - managing the lifecycle of singletons and dependencies
|
||||||
|
|
||||||
|
Copyright (C) Lumiera.org
|
||||||
|
2013, Hermann Vosseler <Ichthyostega@web.de>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2 of
|
||||||
|
the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** @file dependency-factory.hpp
|
||||||
|
** Implementation of a singleton factory used to bring up services as dependency.
|
||||||
|
** @internal this implementation header belongs to our framework to deal with
|
||||||
|
** [service dependencies](\ref depend.hpp) and should not be used directly.
|
||||||
|
** @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef LIB_DEPEND_INJECT_H
|
||||||
|
#define LIB_DEPEND_INJECT_H
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/depend2.hpp"
|
||||||
|
#include "lib/sync-classlock.hpp"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
namespace lib {
|
||||||
|
namespace error = lumiera::error;
|
||||||
|
|
||||||
|
using std::move;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This framework allows to (re)configure the lib::Depend front-end for dependency-injection.
|
||||||
|
* By default, `Depend<TY>` will create a singleton instance of `TY` lazily, on demand.
|
||||||
|
* When instantiating one of the configuration handles provided here -- _prior_ to using
|
||||||
|
* retrieving the instance through `Depend<TY>` -- this default (singleton) behaviour
|
||||||
|
* can be reconfigured in various ways, without the client being aware of it
|
||||||
|
* - instead of a singleton, a service instance with well defined lifecycle can be
|
||||||
|
* exposed through the `Depend<TY>` front-end. When the service is shut down,
|
||||||
|
* clients will receive an exception on access.
|
||||||
|
* - instead of the interface type `TY` mentioned in `Depend<TY>`, an implementation subclass
|
||||||
|
* can be specified, optionally with a closure for the actual creation of this subclass
|
||||||
|
* singleton type, which still happens lazily, on demand
|
||||||
|
* - the current state and configuration can be shadowed temporarily by a test mock instance,
|
||||||
|
* which is managed automatically and removed when leaving the scope of the test.
|
||||||
|
*/
|
||||||
|
template<class SRV>
|
||||||
|
struct DependInject
|
||||||
|
{
|
||||||
|
using Factory = typename Depend<SRV>::Factory;
|
||||||
|
|
||||||
|
|
||||||
|
/** configure dependency-injection for type SRV to build a subclass singleton
|
||||||
|
* @tparam SUB concrete subclass type to build on demand when invoking `Depend<SRV>`
|
||||||
|
* @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already
|
||||||
|
* been invoked at the point when calling this (re)configuration function.
|
||||||
|
*/
|
||||||
|
template<class SUB>
|
||||||
|
static void
|
||||||
|
useSingleton()
|
||||||
|
{
|
||||||
|
__assert_compatible<SUB>();
|
||||||
|
static InstanceHolder<SUB> singleton;
|
||||||
|
installFactory ([&]()
|
||||||
|
{
|
||||||
|
return singleton.buildInstance();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration handle to expose a service implementation through the `Depend<SRV>` front-end.
|
||||||
|
* This noncopyable (but movable) handle shall be planted within the context operating the service
|
||||||
|
* to be exposed. It will immediately create (in RAII style) and manage a heap-allocated instance
|
||||||
|
* of the subclass `IMP` and expose a baseclass pointer to this specific instance through `Depend<SRV>`.
|
||||||
|
* Moreover, the implementation subclass can be accessed through this handle, which acts as smart-ptr.
|
||||||
|
* When the handle goes out of scope, the implementation instance is destroyed and the access through
|
||||||
|
* `Depend<SRV>` is closed and inhibited, to prevent on-demand creation of a baseclass `SRV` singleton.
|
||||||
|
* @tparam IMP concrete service implementation subclass to build, manage and expose.
|
||||||
|
* @throws error::Logic (LUMIERA_ERROR_LIFECYCLE) when the default factory has already
|
||||||
|
* been invoked at the point when calling this (re)configuration function.
|
||||||
|
*/
|
||||||
|
template<class IMP>
|
||||||
|
class ServiceInstance
|
||||||
|
{
|
||||||
|
std::unique_ptr<IMP> instance_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ServiceInstance()
|
||||||
|
: instance_(new IMP{})
|
||||||
|
{
|
||||||
|
__assert_compatible<IMP>();
|
||||||
|
activateServiceAccess (*instance_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ServiceInstance()
|
||||||
|
{
|
||||||
|
deactivateServiceAccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceInstance (ServiceInstance&&) = default;
|
||||||
|
ServiceInstance (ServiceInstance const&) = delete;
|
||||||
|
ServiceInstance& operator= (ServiceInstance&&) = delete;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return bool(instance_);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMP&
|
||||||
|
operator* () const
|
||||||
|
{
|
||||||
|
ENSURE (instance_);
|
||||||
|
return *instance_;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMP*
|
||||||
|
operator-> () const
|
||||||
|
{
|
||||||
|
ENSURE (instance_);
|
||||||
|
return instance_.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration handle for temporarily shadowing a dependency by a test mock instance.
|
||||||
|
* This noncopyable (but movable) handle shall be planted within the immediate test context.
|
||||||
|
* It immediately stashes away the existing state and configuration from `Depend<SRV>`, but
|
||||||
|
* waits for actual invocation of the `Depend<SRV>`-front-end to create a heap-allocated
|
||||||
|
* instance of the `MOC` subclass, which it manages and exposes like a smart-ptr.
|
||||||
|
* When the handle goes out of scope, the original state and configuration is restored
|
||||||
|
*/
|
||||||
|
template<class MOC>
|
||||||
|
class Local
|
||||||
|
{
|
||||||
|
std::unique_ptr<MOC> mock_;
|
||||||
|
|
||||||
|
SRV* origInstance_;
|
||||||
|
Factory origFactory_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Local()
|
||||||
|
{
|
||||||
|
__assert_compatible<MOC>();
|
||||||
|
temporarilyInstallAlternateFactory (origInstance_, origFactory_
|
||||||
|
,[this]()
|
||||||
|
{
|
||||||
|
mock_.reset(new MOC{});
|
||||||
|
return mock_.get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
~Local()
|
||||||
|
{
|
||||||
|
restoreOriginalFactory (origInstance_, origFactory_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Local (Local&&) = default;
|
||||||
|
Local (Local const&) = delete;
|
||||||
|
Local& operator= (Local&&) = delete;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return bool(mock_);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOC&
|
||||||
|
operator* () const
|
||||||
|
{
|
||||||
|
ENSURE (mock_);
|
||||||
|
return *mock_;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOC*
|
||||||
|
operator-> () const
|
||||||
|
{
|
||||||
|
ENSURE (mock_);
|
||||||
|
return mock_.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected: /* ======= internal access-API for those configurations to manipulate Depend<SRV> ======= */
|
||||||
|
template<class IMP>
|
||||||
|
friend class ServiceInstance;
|
||||||
|
template<class MOC>
|
||||||
|
friend class Local;
|
||||||
|
|
||||||
|
|
||||||
|
template<class SUB>
|
||||||
|
static void
|
||||||
|
__assert_compatible()
|
||||||
|
{
|
||||||
|
static_assert (std::is_base_of<SRV,SUB>::value,
|
||||||
|
"Installed implementation class must be compatible to the interface.");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
installFactory (Factory&& otherFac)
|
||||||
|
{
|
||||||
|
ClassLock<SRV> guard;
|
||||||
|
if (Depend<SRV>::instance)
|
||||||
|
throw error::Logic("Attempt to reconfigure dependency injection after the fact. "
|
||||||
|
"The previously installed factory (typically Singleton) was already used."
|
||||||
|
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||||
|
Depend<SRV>::factory = move (otherFac);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
temporarilyInstallAlternateFactory (SRV*& stashInstance, Factory& stashFac, Factory&& newFac)
|
||||||
|
{
|
||||||
|
ClassLock<SRV> guard;
|
||||||
|
stashFac = move(Depend<SRV>::factory);
|
||||||
|
stashInstance = Depend<SRV>::instance;
|
||||||
|
Depend<SRV>::factory = move(newFac);
|
||||||
|
Depend<SRV>::instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
restoreOriginalFactory (SRV*& stashInstance, Factory& stashFac)
|
||||||
|
{
|
||||||
|
ClassLock<SRV> guard;
|
||||||
|
Depend<SRV>::factory = move(stashFac);
|
||||||
|
Depend<SRV>::instance = stashInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
activateServiceAccess (SRV& newInstance)
|
||||||
|
{
|
||||||
|
ClassLock<SRV> guard;
|
||||||
|
if (Depend<SRV>::instance)
|
||||||
|
throw error::Logic("Attempt to activate an external service implementation, "
|
||||||
|
"but another instance has already been dependency-injected."
|
||||||
|
, error::LUMIERA_ERROR_LIFECYCLE);
|
||||||
|
Depend<SRV>::instance = &newInstance;
|
||||||
|
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
deactivateServiceAccess()
|
||||||
|
{
|
||||||
|
ClassLock<SRV> guard;
|
||||||
|
Depend<SRV>::instance = nullptr;
|
||||||
|
Depend<SRV>::factory = Depend<SRV>::disabledFactory;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace lib
|
||||||
|
#endif
|
||||||
|
|
@ -81,17 +81,76 @@ This code is heavily inspired by
|
||||||
#define WIP_LIB_DEPEND_H
|
#define WIP_LIB_DEPEND_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "lib/error.hpp"
|
||||||
|
#include "lib/nobug-init.hpp"
|
||||||
#include "lib/sync-classlock.hpp"
|
#include "lib/sync-classlock.hpp"
|
||||||
#include "lib/dependency-factory2.hpp"
|
#include "lib/meta/util.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
|
namespace error = lumiera::error;
|
||||||
|
|
||||||
|
|
||||||
|
namespace { // Implementation helper...
|
||||||
|
|
||||||
|
using lib::meta::enable_if;
|
||||||
|
|
||||||
|
template<typename TAR, typename SEL =void>
|
||||||
|
class InstanceHolder
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
std::unique_ptr<TAR> instance_;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
TAR*
|
||||||
|
buildInstance()
|
||||||
|
{
|
||||||
|
if (instance_)
|
||||||
|
throw error::Fatal("Attempt to double-create a singleton service. "
|
||||||
|
"Either the application logic, or the compiler "
|
||||||
|
"or runtime system is seriously broken"
|
||||||
|
,error::LUMIERA_ERROR_LIFECYCLE);
|
||||||
|
|
||||||
|
// place new instance into embedded buffer
|
||||||
|
instance_.reset (new TAR{});
|
||||||
|
return instance_.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ABS>
|
||||||
|
class InstanceHolder<ABS, enable_if<std::is_abstract<ABS>>>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ABS*
|
||||||
|
buildInstance()
|
||||||
|
{
|
||||||
|
throw error::Fatal("Attempt to create a singleton instance of an abstract class. "
|
||||||
|
"Application architecture or lifecycle is seriously broken.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}//(End)Implementation helper
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal access point to reconfigure dependency injection on a per type base
|
||||||
|
* @see depend-inject.hpp
|
||||||
|
*/
|
||||||
|
template<class SRV>
|
||||||
|
class DependInject;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Access point to singletons and other kinds of dependencies.
|
* Access point to singletons and other kinds of dependencies.
|
||||||
* Actually this is a Factory object, which is typically placed into a
|
* Actually this is a Factory object, which is typically placed into a
|
||||||
* static field of the Singleton (target) class or some otherwise suitable interface.
|
* static field of the Singleton (target) class or some otherwise suitable interface.
|
||||||
* @param SI the class of the Singleton instance
|
* @tparam SRV the class of the Service or Singleton instance
|
||||||
* @note uses static fields internally, so all factory configuration is shared per type
|
* @note uses static fields internally, so all factory configuration is shared per type
|
||||||
* @remark there is an ongoing discussion regarding the viability of the
|
* @remark there is an ongoing discussion regarding the viability of the
|
||||||
* Double Checked Locking pattern, which requires either the context of a clearly defined
|
* Double Checked Locking pattern, which requires either the context of a clearly defined
|
||||||
|
|
@ -110,106 +169,70 @@ namespace lib {
|
||||||
* @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway /////////////////////TICKET #1086
|
* @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway /////////////////////TICKET #1086
|
||||||
* @param SI the class of the Singleton instance
|
* @param SI the class of the Singleton instance
|
||||||
*/
|
*/
|
||||||
template<class SI>
|
template<class SRV>
|
||||||
class Depend2
|
class Depend
|
||||||
{
|
{
|
||||||
typedef ClassLock<SI> SyncLock;
|
using Factory = std::function<SRV*()>;
|
||||||
|
|
||||||
static SI* volatile instance;
|
static SRV* instance;
|
||||||
static DependencyFactory2 factory;
|
static Factory factory;
|
||||||
|
|
||||||
|
static InstanceHolder<SRV> singleton;
|
||||||
|
|
||||||
|
friend class DependInject<SRV>;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Interface to be used by clients for retrieving the service instance.
|
/** Interface to be used by clients for retrieving the service instance.
|
||||||
* Manages the instance creation, lifecycle and access in multithreaded context.
|
* Manages the instance creation, lifecycle and access in multithreaded context.
|
||||||
* @return instance of class SI. When used in default configuration,
|
* @return instance of class `SRV`. When used in default configuration,
|
||||||
* this service instance is a singleton
|
* this service instance is a singleton
|
||||||
*/
|
*/
|
||||||
SI&
|
SRV&
|
||||||
operator() ()
|
operator() ()
|
||||||
{
|
{
|
||||||
if (!instance)
|
if (!instance)
|
||||||
{
|
retrieveInstance();
|
||||||
SyncLock guard;
|
|
||||||
|
|
||||||
if (!instance)
|
|
||||||
instance = static_cast<SI*> (factory.buildInstance());
|
|
||||||
}
|
|
||||||
ENSURE (instance);
|
ENSURE (instance);
|
||||||
return *instance;
|
return *instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
typedef DependencyFactory2::InstanceConstructor Constructor;
|
retrieveInstance()
|
||||||
|
|
||||||
|
|
||||||
/** default configuration of the dependency factory
|
|
||||||
* is to build a singleton instance on demand */
|
|
||||||
Depend2()
|
|
||||||
{
|
{
|
||||||
factory.ensureInitialisation (buildSingleton2<SI>());
|
ClassLock<SRV> guard;
|
||||||
|
|
||||||
|
if (!instance)
|
||||||
|
{
|
||||||
|
if (!factory)
|
||||||
|
instance = singleton.buildInstance();
|
||||||
|
else
|
||||||
|
instance = factory();
|
||||||
|
factory = disabledFactory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static SRV*
|
||||||
* optionally, the instance creation process can be configured explicitly
|
disabledFactory()
|
||||||
* \em once per type. By default, a singleton instance will be created.
|
|
||||||
* Installing another factory function enables other kinds of dependency injection;
|
|
||||||
* this configuration must be done \em prior to any use the dependency factory.
|
|
||||||
* @param ctor a constructor function, which will be invoked on first usage.
|
|
||||||
* @note basically a custom constructor function is responsible to manage any
|
|
||||||
* created service instances.
|
|
||||||
* @remark typically the \c Depend<TY> factory will be placed into a static variable,
|
|
||||||
* embedded into another type or interface. In this case, actual storage for
|
|
||||||
* this static variable needs to be allocated within some translation unit.
|
|
||||||
* And this is the point where this ctor will be invoked, in the static
|
|
||||||
* initialisation phase of the respective translation unit (*.cpp)
|
|
||||||
*/
|
|
||||||
Depend2 (Constructor ctor)
|
|
||||||
{
|
{
|
||||||
factory.installConstructorFunction (ctor);
|
throw error::Fatal("Service not available at this point of the Application Lifecycle"
|
||||||
}
|
,error::LUMIERA_ERROR_LIFECYCLE);
|
||||||
|
|
||||||
// standard copy operations applicable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* === Management / Test support interface === */
|
|
||||||
|
|
||||||
/** temporarily replace the service instance.
|
|
||||||
* The purpose of this operation is to support unit testing.
|
|
||||||
* @param mock reference to an existing service instance (mock).
|
|
||||||
* @return reference to the currently active service instance.
|
|
||||||
* @warning this is a dangerous operation and not threadsafe.
|
|
||||||
* Concurrent accesses might still get the old reference;
|
|
||||||
* the only way to prevent this would be to synchronise
|
|
||||||
* \em any access (which is too expensive).
|
|
||||||
* This feature should only be used for unit tests thusly.
|
|
||||||
* @remark the replacement is not actively managed by the DependencyFactory,
|
|
||||||
* it remains in ownership of the calling client (unit test). Typically
|
|
||||||
* this test will keep the returned original service reference and
|
|
||||||
* care for restoring the original state when done.
|
|
||||||
* @see Depend4Test scoped object for automated test mock injection
|
|
||||||
*/
|
|
||||||
static SI*
|
|
||||||
injectReplacement (SI* mock)
|
|
||||||
{
|
|
||||||
SyncLock guard;
|
|
||||||
SI* currentInstance = instance;
|
|
||||||
instance = mock;
|
|
||||||
return currentInstance;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Storage for static per type instance management...
|
/* === allocate Storage for static per type instance management === */
|
||||||
template<class SI>
|
template<class SRV>
|
||||||
SI* volatile Depend2<SI>::instance;
|
SRV* Depend<SRV>::instance;
|
||||||
|
|
||||||
template<class SI>
|
template<class SRV>
|
||||||
DependencyFactory2 Depend2<SI>::factory;
|
typename Depend<SRV>::Factory Depend<SRV>::factory;
|
||||||
|
|
||||||
|
template<class SRV>
|
||||||
|
InstanceHolder<SRV> Depend<SRV>::singleton;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
/*
|
|
||||||
DEPENDENCY-FACTORY.hpp - managing the lifecycle of singletons and dependencies
|
|
||||||
|
|
||||||
Copyright (C) Lumiera.org
|
|
||||||
2013, Hermann Vosseler <Ichthyostega@web.de>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU General Public License as
|
|
||||||
published by the Free Software Foundation; either version 2 of
|
|
||||||
the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/** @file dependency-factory.hpp
|
|
||||||
** Implementation of a singleton factory used to bring up services as dependency.
|
|
||||||
** @internal this implementation header belongs to our framework to deal with
|
|
||||||
** [service dependencies](\ref depend.hpp) and should not be used directly.
|
|
||||||
** @todo WIP-WIP 3/18 rework of the singleton / dependency factory is underway
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef WIP_LIB_DEPENDENCY_FACTORY_H
|
|
||||||
#define WIP_LIB_DEPENDENCY_FACTORY_H
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "lib/nobug-init.hpp"
|
|
||||||
#include "lib/error.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
|
||||||
|
|
||||||
namespace error = lumiera::error;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal Factory to generate and manage service objects classified by type.
|
|
||||||
* An instance of this factory is placed <i>once for each type</i> for use by
|
|
||||||
* the lib::Depend<TY> front-end for dependency management. While the latter
|
|
||||||
* provides the singleton-style initialisation patter, the DependencyFacotry
|
|
||||||
* maintains a customisable factory function for instance creation. Moreover,
|
|
||||||
* the embedded helper template DependencyFactory::InstanceHolder actually
|
|
||||||
* creates and manages the singleton instances in default configuration;
|
|
||||||
* it is placed into a function-scope static variable; consequently
|
|
||||||
* the singleton instances are placed into static memory by default.
|
|
||||||
*/
|
|
||||||
class DependencyFactory2
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef void* (*InstanceConstructor)(void);
|
|
||||||
|
|
||||||
/** ensure initialisation by installing a default constructor function,
|
|
||||||
* but don't change an explicitly installed different constructor function.
|
|
||||||
* @remark deliberately this DependencyFactory has no constructor to
|
|
||||||
* initialise the object field \c ctorFunction_ to zero.
|
|
||||||
* The reason is, in the intended usage scenario, the
|
|
||||||
* DependencyFactory lives within a static variable,
|
|
||||||
* which might be constructed in no defined order
|
|
||||||
* in relation to the Depend<TY> instance.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
ensureInitialisation (InstanceConstructor defaultCtor)
|
|
||||||
{
|
|
||||||
if (!ctorFunction_)
|
|
||||||
this->ctorFunction_ = defaultCtor;
|
|
||||||
ENSURE (ctorFunction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** explicitly set up constructor function, unless already configured
|
|
||||||
* In the default configuration, the template \c Depend<TY> installs a
|
|
||||||
* builder function to create a singleton instance in static memory.
|
|
||||||
* But specific instances might install e.g. a factory to create a
|
|
||||||
* implementation defined subclass; this might also be the place
|
|
||||||
* to hook in some kind of centralised service manager in future.
|
|
||||||
* @param ctor a function to be invoked to create a new service instance
|
|
||||||
* @throw error::Fatal when attempting to change an existing configuration.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
installConstructorFunction (InstanceConstructor ctor)
|
|
||||||
{
|
|
||||||
if (ctorFunction_ && ctor != ctorFunction_)
|
|
||||||
throw error::Fatal ("DependencyFactory: attempt to change the instance builder function "
|
|
||||||
"after-the-fact. Before this call, a different function was installed "
|
|
||||||
"and possibly also used already. Hint: visit all code locations, which "
|
|
||||||
"actually create an instance of the Depend<TY> template."
|
|
||||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
this->ctorFunction_ = ctor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** invoke the installed ctor function */
|
|
||||||
void*
|
|
||||||
buildInstance()
|
|
||||||
{
|
|
||||||
if (!ctorFunction_)
|
|
||||||
throw error::Fatal ("lib::Depend: attempt to retrieve a service object prior to initialisation "
|
|
||||||
"of the DependencyFactory. Typically, this happens due to a misconfiguration "
|
|
||||||
"regarding static initialisation order. When lib::Depend<TY> is placed into "
|
|
||||||
"a class static variable, then the definition and initialisation of that "
|
|
||||||
"variable must happen prior to the call which caused this exception."
|
|
||||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
return ctorFunction_();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
/** pointer to the concrete function
|
|
||||||
* used for building new service instances */
|
|
||||||
InstanceConstructor ctorFunction_;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** function to build service instances.
|
|
||||||
* A service class with private ctor can declare DependencyFactory as friend,
|
|
||||||
* to indicate this is the expected way to create instances */
|
|
||||||
template<typename TAR>
|
|
||||||
static TAR*
|
|
||||||
create_in_buffer (void* buffer)
|
|
||||||
{
|
|
||||||
return new(buffer) TAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal Helper to manage a service instance within an embedded buffer.
|
|
||||||
* This helper and thus the service instance will be allocated into static memory.
|
|
||||||
*/
|
|
||||||
template<typename TAR>
|
|
||||||
class InstanceHolder
|
|
||||||
: boost::noncopyable
|
|
||||||
{
|
|
||||||
/** storage for the service instance */
|
|
||||||
char buff_[sizeof(TAR)];
|
|
||||||
uint lifecycle_;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
InstanceHolder()
|
|
||||||
: lifecycle_(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
~InstanceHolder()
|
|
||||||
{
|
|
||||||
lifecycle_ |= 4;
|
|
||||||
if (1 & lifecycle_)
|
|
||||||
{
|
|
||||||
reinterpret_cast<TAR&> (buff_). ~TAR();
|
|
||||||
--lifecycle_;
|
|
||||||
} }
|
|
||||||
|
|
||||||
|
|
||||||
TAR*
|
|
||||||
buildInstance ()
|
|
||||||
{
|
|
||||||
if (0 < lifecycle_)
|
|
||||||
throw error::Fatal("Attempt to double-create a singleton service. "
|
|
||||||
"Either the application logic, or the compiler "
|
|
||||||
"or runtime system is seriously broken"
|
|
||||||
,error::LUMIERA_ERROR_LIFECYCLE);
|
|
||||||
|
|
||||||
// place new instance into embedded buffer
|
|
||||||
TAR* newInstance = create_in_buffer<TAR>(buff_);
|
|
||||||
++lifecycle_;
|
|
||||||
return newInstance;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<class TAR>
|
|
||||||
static void*
|
|
||||||
createSingletonInstance()
|
|
||||||
{
|
|
||||||
static InstanceHolder<TAR> storage; // note: the singleton(s) live here
|
|
||||||
return storage.buildInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<class TAR>
|
|
||||||
friend InstanceConstructor buildSingleton2();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DSL-style marker function for client code
|
|
||||||
* to configure the usage of a specific subclass.
|
|
||||||
* Typically this function is used right within the
|
|
||||||
* Constructor call for lib::Depend; this allows to
|
|
||||||
* confine the actual service implementation class
|
|
||||||
* to a single compilation unit, without the need
|
|
||||||
* for clients of the respective service to know
|
|
||||||
* the actual concrete implementation class
|
|
||||||
*/
|
|
||||||
template<class TAR>
|
|
||||||
inline DependencyFactory2::InstanceConstructor
|
|
||||||
buildSingleton2()
|
|
||||||
{
|
|
||||||
return & DependencyFactory2::createSingletonInstance<TAR>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace lib
|
|
||||||
#endif
|
|
||||||
|
|
@ -27027,7 +27027,7 @@
|
||||||
<node CREATED="1521242824239" ID="ID_1013563756" MODIFIED="1521242834313" TEXT="inline-wegwerf-Objekt"/>
|
<node CREATED="1521242824239" ID="ID_1013563756" MODIFIED="1521242834313" TEXT="inline-wegwerf-Objekt"/>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1521240053852" ID="ID_666003564" MODIFIED="1521418339291" STYLE="fork" TEXT="Detail-Fragen">
|
<node CREATED="1521240053852" FOLDED="true" ID="ID_666003564" MODIFIED="1521418511493" STYLE="fork" TEXT="Detail-Fragen">
|
||||||
<icon BUILTIN="button_ok"/>
|
<icon BUILTIN="button_ok"/>
|
||||||
<node COLOR="#990000" CREATED="1521169099047" ID="ID_1183392158" MODIFIED="1521418281285" TEXT="DependencyFactory-Rahmenklasse">
|
<node COLOR="#990000" CREATED="1521169099047" ID="ID_1183392158" MODIFIED="1521418281285" TEXT="DependencyFactory-Rahmenklasse">
|
||||||
<icon BUILTIN="button_cancel"/>
|
<icon BUILTIN="button_cancel"/>
|
||||||
|
|
@ -27191,12 +27191,59 @@
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160691830" ID="ID_53329830" MODIFIED="1521160718843" TEXT="Implementierung ausführen">
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160691830" ID="ID_53329830" MODIFIED="1521160718843" TEXT="Implementierung ausführen">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418559868" ID="ID_592269917" MODIFIED="1521418569579" TEXT="Depend<SRV> Front-End">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418571594" ID="ID_1432227459" MODIFIED="1521418597112" TEXT="DependInject<SRV> ersetzt dependency-factory.hpp">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418598463" ID="ID_237484313" MODIFIED="1521418617389" TEXT="Objekterzeugung erweitern">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418618052" ID="ID_1253147848" MODIFIED="1521418626516" TEXT="auf beliebige Argumente">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418627130" ID="ID_206705145" MODIFIED="1521418639801" TEXT="auf allgemeine Closures">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160700669" ID="ID_978221585" MODIFIED="1521160718035" TEXT="Dokumentation">
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160700669" ID="ID_978221585" MODIFIED="1521160718035" TEXT="Dokumentation">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="flag-yellow"/>
|
||||||
</node>
|
</node>
|
||||||
</node>
|
</node>
|
||||||
<node CREATED="1520722160591" ID="ID_135546699" MODIFIED="1520722168810" TEXT="Unit-Test"/>
|
<node CREATED="1520722160591" ID="ID_135546699" MODIFIED="1520722168810" TEXT="Unit-Test">
|
||||||
|
<node CREATED="1521419576303" ID="ID_726128967" MODIFIED="1521419582850" TEXT="alte Tests">
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521418937870" ID="ID_906296527" MODIFIED="1521418940664" TEXT="Singleton_test">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419045649" ID="ID_839295062" MODIFIED="1521419048033" TEXT="SingletonSubclass_test">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419134046" ID="ID_1638082455" MODIFIED="1521419136661" TEXT="SingletonTestMock_test">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419300765" ID="ID_417098537" MODIFIED="1521419778465" TEXT="DependencyFactory_test">
|
||||||
|
<linktarget COLOR="#b9274f" DESTINATION="ID_417098537" ENDARROW="Default" ENDINCLINATION="159;18;" ID="Arrow_ID_606259386" SOURCE="ID_914313570" STARTARROW="Default" STARTINCLINATION="18;-57;"/>
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419730442" HGAP="42" ID="ID_657803548" MODIFIED="1521419793254" TEXT="nach der Umstellung etwas straffen" VSHIFT="-13">
|
||||||
|
<icon BUILTIN="yes"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521419563657" ID="ID_733291707" MODIFIED="1521419571641" TEXT="DependencyConfiguration_test">
|
||||||
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
<node CREATED="1521419590709" ID="ID_914313570" MODIFIED="1521419778465" TEXT="ziemlich redundant">
|
||||||
|
<arrowlink COLOR="#b9274f" DESTINATION="ID_417098537" ENDARROW="Default" ENDINCLINATION="159;18;" ID="Arrow_ID_606259386" STARTARROW="Default" STARTINCLINATION="18;-57;"/>
|
||||||
|
<node CREATED="1521419624065" HGAP="29" ID="ID_477987875" MODIFIED="1521419637660" TEXT="weiterhin sinnvoll?" VSHIFT="14">
|
||||||
|
<node CREATED="1521419638943" ID="ID_717613154" MODIFIED="1521419705073" TEXT="TDD während der re-Implementirung"/>
|
||||||
|
<node CREATED="1521419657356" ID="ID_1381265753" MODIFIED="1521419674957" TEXT="der alte Test muß weiterhin laufen (ohne nachzudenken!)"/>
|
||||||
|
<node CREATED="1521419676825" ID="ID_92062482" MODIFIED="1521419693987" TEXT="mehr Tests schaden nie">
|
||||||
|
<icon BUILTIN="ksmiletris"/>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
<node CREATED="1520722155112" ID="ID_1512641426" MODIFIED="1520722159028" TEXT="Integration">
|
<node CREATED="1520722155112" ID="ID_1512641426" MODIFIED="1520722159028" TEXT="Integration">
|
||||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160755182" ID="ID_678080556" MODIFIED="1521160802831" TEXT="Umbenennen">
|
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1521160755182" ID="ID_678080556" MODIFIED="1521160802831" TEXT="Umbenennen">
|
||||||
<icon BUILTIN="flag-yellow"/>
|
<icon BUILTIN="flag-yellow"/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue