c# - Only allow Factory method to instantiate objects (prevent intantiation of base class AND unitialized objects) -
i have base class handling "jobs". factory method creates derived "job handler" objects according job type , ensures job handler objects initialized job information.
calling factory method request handler job , person assigned:
public enum job { clean, cook, cookchicken }; // list of jobs. static void main(string[] args) { handlerbase handler; handler = handlerbase.createjobhandler(job.cook, "bob"); handler.dojob(); handler = handlerbase.createjobhandler(job.clean, "alice"); handler.dojob(); handler = handlerbase.createjobhandler(job.cookchicken, "sue"); handler.dojob(); } the result:
bob cooking. alice cleaning. sue cooking. sue cooking chicken. job handler classes:
public class cleanhandler : handlerbase { protected cleanhandler(handlerbase handler) : base(handler) { } public override void dojob() { console.writeline("{0} cleaning.", person); } } public class cookhandler : handlerbase { protected cookhandler(handlerbase handler) : base(handler) { } public override void dojob() { console.writeline("{0} cooking.", person); } } a sub-classed job handler:
public class cookchickenhandler : cookhandler { protected cookchickenhandler(handlerbase handler) : base(handler) { } public override void dojob() { base.dojob(); console.writeline("{0} cooking chicken.", person); } } the best way of doing things? have struggled these issues:
- ensure derived objects have initialized base object (person assigned).
- prevent instantiation of objects other through factory method performs initialization.
- prevent instantiation of base class object.
the job handler handlerbase base class:
- a
dictionary<job,type>maps jobs handler classes. - private setter job data (i.e., person) prevents access except factory method.
- no default constructor , private constructor prevents construction except factory method.
- a protected "copy constructor" non-private constructor. 1 must have instantiated handlerbase create new object, , base class factory can create base handlerbase object. "copy constructor" throws exception if 1 attempts create new objects non-base object (again, preventing construction except factory method).
a @ base class:
public class handlerbase { // dictionary maps job proper handlerbase type. private static dictionary<job, type> registeredhandlers = new dictionary<job, type>() { { job.clean, typeof(cleanhandler) }, { job.cook, typeof(cookhandler) }, { job.cookchicken, typeof(cookchickenhandler) } }; // person assigned job. private setter accessible factory method. public string person { get; private set; } // private constructor data initialization accessible factory method. private handlerbase(string name) { this.person = name; } // non-private "copy constructor" requires initialized base object. // factory method can make handlerbase object. protected handlerbase(handlerbase handler) { // prevent creating new objects non-base objects. if (handler.gettype() != typeof(handlerbase)) throw new argumentexception("that's illegal, pal!"); this.person = handler.person; // peform "copy" } // factory method. public static handlerbase createjobhandler(job job, string name) { // job handler in dictionary. type handlertype = registeredhandlers[job]; // create "seed" base object enable calling derived constructor. handlerbase seed = new handlerbase(name); object[] args = new object[] { seed }; bindingflags flags = bindingflags.instance | bindingflags.nonpublic; handlerbase newinstance = (handlerbase)activator .createinstance(handlertype, flags, null, args, null); return newinstance; } public virtual void dojob() { throw new notimplementedexception(); } } a @ factory method:
because have made public construction of new object impossible without having instantiated base object, factory method first constructs handlerbase instance "seed" calling needed derived class "copy constructor".
the factory method uses activator.createinstance() instantiate new objects. activator.createinstance() looks constructor matches requested signature:
the desired constructor derivedhandler(handlerbase handler), thus,
- my "seed"
handlerbaseobject placed inobject[] args. - i combine
bindingflags.instance,bindingflags.nonpubliccreateinstance() searches non-public constructor (addbindingflags.publicfind public constructor). - activator.createinstance() instantiates new object returned.
what don't like...
constructors not enforced when implementing interface or class. constructor code in derived classes mandatory:
protected derivedjobhandler(handlerbase handler) : base(handler) { } yet, if constructor left out don't friendly compiler error telling exact method signature needed: "'derivedjobhandler' not contain constructor takes 0 arguments".
it possible write constructor eliminates compiler error, instead--worse!--resulting in run-time error:
protected derivedjobhandler() : base(null) { } i not there no means of enforcing required constructor in derived class implementations.
any ideas best-practices appreciated!
i see 3 responsibilities in handlerbase that, if decoupled 1 another, may simplify design problem.
- registration of handlers
- construction of handlers
- dojob
one way of reorganizing put #1 , #2 on factory class, , #3 on class internal constructor factory class can call per internal requirements. can pass in person , job values directly rather letting constructor pull them different handlerbase instance, make code easier understand.
once these responsibilities separated, can more evolve them independently.
Comments
Post a Comment