300 likes | 565 Views
Advance Patterns and Frameworks Frameworker's Dilemma. Peter Sommerlad, SS2005. Overview. stable Interfaces vs. Framework Evolution forces pulling in opposite directions simple stable and generic Interfaces configurability Eclipse's approach dilemma explanation plug-in architecture
E N D
Advance Patterns and FrameworksFrameworker's Dilemma Peter Sommerlad, SS2005
Overview • stable Interfaces vs. Framework Evolution • forces pulling in opposite directions • simple stable and generic Interfaces • configurability • Eclipse's approach • dilemma explanation • plug-in architecture • extension interface Frameworker's Dilemmas
From Procedural towards Framework Programming • source: Inside Taligent Technology Frameworker's Dilemmas
Proposed benefits of application frameworks • Less code to write. • More reliable and robust code. • More consistent and modular code. • More focus on areas of expertise, less focus on areas of system compatibility. • Generic solutions that can be reused for other, related problems. • Improved maintenance and orderly program evolution. • Improved integration of related programs. • source: Inside Taligent Technology 1995 Frameworker's Dilemmas
Framework usage • Framework users implement application code by subclassing of framework classes or implementing their predefined interfaces by application components. • configuration of predefined components as well, but that is more like library usage. • Inheritance is one of the strongest couplings between 2 components • why? • what can happen? Frameworker's Dilemmas
Application lock-in ? • strong coupling to framework components hinders application portability: • How do I separate my application specific code and concepts from too many details of the framework, so that I can port it to the next version of the framework or even another environment? • own thin decoupling layer • couple to framework environment nevertheless • build my own portable framework Frameworker's Dilemmas
Framework lock-in • There are two reasons for lack of framework evolution and improvement: • no application uses the framework • no users, no need to improve or evolve • no experience on what to improve • one or more applications use the framework • changing the framework risks breaking applications • applications produce work-arounds to framework deficiencies and break when those "features" are fixed • Frameworker's Dilemma Frameworker's Dilemmas
Potential Ways out of the Frameworker's Dilemma (1) • Think very hard up-front • very very hard, may work a little bit the second or third time • requires very experienced, very bright persons • without concrete applications, it is hard to decide what to abstract and generalize in a framework • can lead to expensive and unusable over-engineered frameworks (e.g. Taligent, IBM's San Francisco) • takes too long to hit window of opportunity • might still be sub-optimal to use Frameworker's Dilemmas
Potential Ways out of the Frameworker's Dilemma (2) • Don't care too much about your framework users (= application developers) • lay the burden of porting applications to the application's developer • provide many good and useful new features to make porting a "must" • might provide porting tools and guidelines • support old versions indefinitely or fight hard to keep it backward compatible • framework user's might switch to other offerings Frameworker's Dilemmas
Potential Ways out of the Frameworker's Dilemma (3) • Be a good citizen and let framework users participate • social process can help, e.g. by giving users time to migrate • deprecated interfaces • tendency for indefinite backward compatibility • with a strong user community • design by commitee is almost always over-engineered and brittle • might require explicit migration training and coaching Frameworker's Dilemmas
Potential Ways out of the Frameworker's Dilemma (4) • Can technology or methodology help? • not totally, but can improve situation • simple interfaces • tendency to be more stable • flexible interfaces • tendency to be more stable • configurability • less direct code-dependencies • patterns • extension interface Frameworker's Dilemmas
Simple InterfacesWebDisplay's Action class class Action : public NotCloned { public: Action(const char *name); virtual bool DoAction(String &transitionToken, Context&) { return false; } virtual bool DoExecAction(String &transitionToken, Context& ctx, const ROAnything &config) { return DoAction(transitionToken,ctx); } static bool ExecAction(String &transitionToken, Context &c); static bool ExecAction(String &transitionToken, Context &c, const ROAnything &config); RegCacheDef(Action); // FindAction() }; • Only DoAction() needs to be implemented • two simple generic parameters Frameworker's Dilemmas
Simple InterfacesWebDisplay's Renderer class Renderer : public NotCloned { public: Renderer(const char *name); //!rendering hook; overwrite this method in subclasses //!generates output on reply driven by config using context //! \param reply stream to generate output on //! \param c Context to be used for output generation //! \param config configuration which drives the output virtual void RenderAll(ostream &reply, Context &c, const ROAnything &config); //... some scaffolding methods non-virtual • Only RenderAll() needs to be implemented • three simple generic parameters • Simple constructor needing a name Frameworker's Dilemmas
Simple Interfacesjunit public abstract class TestCase implements Test { ... public void run() { setUp(); runTest(); tearDown(); } protected void runTest() throws Throwable {} protected void setUp() throws Exception {} protected void tearDown() throws Exception {} ... } • Test asks for void run() • most simple method • TestCase for three similar simple methods • reflection allows all void testXXX() methods Frameworker's Dilemmas
Flexible Interfaces Encapsulate Context Pattern (Allan Kelly) • A system contains data, which must be generally available to divergent parts of the system, but we wish to avoid using long parameter lists to functions or global data; therefore, we place the necessary data in a Context container and pass this object from function to function. Other patterns: • Property List, Anything • implementation options for Encapsulate Context Frameworker's Dilemmas
Encapsulate Context Frameworker's Dilemmas
Property List as Parameters • Property Lists as parameters. Passing a property list as a parameter allows a client component to extend the amount of data passed to an operation at will. If you want to keep stable interfaces, shared by different teams, but are still heavily under development, propertylists as parameters or attributes can be a real life-safer. • see also Anything benefits! Frameworker's Dilemmas
Flexible Plug Ins • Eclipse also suffers from the Frameworker's Dilemma. • However, Kent Beck and Erich Gamma explain in their book Contributing to Eclipse how Eclipse's framework leverages patterns to overcome this dilemma • Note: examples from this book are based on Release 2.x of Eclipse, 3.x uses similar patterns but some different classes/interfaces Frameworker's Dilemmas
Eclipse (2.x) Plugin Exampleaus [BeckGamma] • org.eclipse.resources provides abstractions like IFile, IFolder, IProject • separation of concept from UI • developing a File class requires to implement IFile as well as a corresponding UI interface, e.g., IPropertySource for the properties editor • IFile could extend IPropertySource but • results in a bloated interface • implementing such a service is an implementation detail, which breaks the API of IFile if added later on • IFile shouldn't have to know about the Properties view Frameworker's Dilemmas
Example for Extension Interface Eclipse needs a mechanism that allows • adding a service interface to a component without exposing it in the component's type • allowing future feature additions/changes without breaking client code • adding behavior to preexisting types such as IFile • allow even unforseen extensions of a component Frameworker's Dilemmas
Extension Interface Pattern • The Extension Interface design pattern allows multiple interfaces to be exported by a component, to prevent bloating of interfaces and breaking of client code when developers extend or modify the functionality of the component. Frameworker's Dilemmas
Eclipse's IAdaptable • IAdaptable defines a Method • Object getAdapter(Class a) • corresponds to getExtension of the Extension Interface pattern • usage: privateIPropertySourcegetPropertySource(Objectobject){ //... elseif(objectinstanceofIAdaptable) result=(IPropertySource) ((IAdaptable)object).getAdapter(IPropertySource.class); sources.put(object,result); returnresult; } Frameworker's Dilemmas
IAdaptable usage in Eclipse • A class wants to provide additional interfaces without exposing them in the API • getAdapter implemented internally in the class • adding a new interface requires modification of getAdapter implementation • A class is augmented from the outside to provide additional services • no code change required publicclassFileSystemElementimplementsIAdaptable{ //... publicObjectgetAdapter(Classadapter){ if(adapter==IWorkbenchAdapter.class){ returnworkbenchAdapter; } //defer to the platform returnPlatform.getAdapterManager().getAdapter(this,adapter); } Frameworker's Dilemmas
How to provide configurable adaptation • IAdapterManager – AdapterManager • keeps registry of available AdapterFactories for a specific IAdaptable class • IAdapterFactory • creates an IAdaptable for a given object together with a given target IAdaptable subclass • Eclipse 3.x contains an IAdapterFactoryProxy, so that Factories are loaded on demand like configured publicObjectgetAdapter(Classadapter){ returnPlatform.getAdapterManager().getAdapter(this,adapter); } Frameworker's Dilemmas
Example Adapter Factory (Eclipse 2.1) classWorkbenchAdapterFactoryimplementsIAdapterFactory{ //... publicObjectgetAdapter(Objecto,ClassadapterType){ if(adapterType.isInstance(o)){ returno; } if(adapterType==IWorkbenchAdapter.class){ returngetWorkbenchElement(o); } if(adapterType==IPersistableElement.class){ returngetPersistableElement(o); } if(adapterType==IElementFactory.class){ returngetElementFactory(o); } if(adapterType==IActionFilter.class){ returngetActionFilter(o); } returnnull; } Frameworker's Dilemmas
IAdaptable PlatformObject getAdapter(Class) getAdapter(Class) IAdapterManager getAdapter(Object,Class) Patterns dealing with IAdaptable RootInterface ExtensionInterface * SomeInterface Abstract Factory * IAdapterFactory getAdapter(Object,Class) Component Manager AdapterFactoryProxy Proxy getAdapter(Object,Class) Frameworker's Dilemmas
Discussion Eclipse's Extension Mechanism • Multiple Adapters for the same type • most specific adapter wins following class/interface hierarchy • Stateless Adapters are easier to manage • better have stateless adapters, keep state in the adapted objects • what pattern is usable for keeping such flexible state? • Which adapters are supported? • only feasible by documenting and try&error • Adapter negotiation might be needed • different adapters can be usable in a concrete situation • Reduced programming comfort • casting and null checks required Frameworker's Dilemmas
Searching Eclipse for patterns... • create a plug-in project (for example) • and/or import org.eclipse packages, e.g. core, jdt.core, etc. • Navigate->Open Type • try pattern matching: • *visitor • *adaptor • *proxy • *factory Frameworker's Dilemmas
Literatur • Kent Beck, Erich Gamma: Contributing to Eclipse: Principles, Patterns and Plug-Ins Frameworker's Dilemmas