Exercises in Programming Style–Plugins

NOTE : read the rest of the series, or check out the source code.

If you enjoy read­ing these exer­cises then please buy Crista’s book to sup­port her work.

exercises-prog-styles-cover

Fol­low­ing on from the last post, we will look at the Plu­g­ins style today.

 

Style 19 – Plugins

Constraints

  • The prob­lem is decom­posed using some form of abstrac­tion (pro­ce­dures, func­tions, objects, etc.).
  • All or some of the abstrac­tions are phys­i­cal­ly encap­su­lat­ed into their own, usu­al­ly pre-com­piled, pack­ages. Main pro­gram and each of the pack­ages are com­piled inde­pen­dent­ly. These pack­ages are loaded dynam­i­cal­ly by the main pro­gram, usu­al­ly in the begin­ning (but not nec­es­sar­i­ly).
  • Main pro­gram uses functions/objects from the dynam­i­cal­ly-loaded pack­ages, with­out know­ing which exact imple­men­ta­tion will be used. New imple­men­ta­tions can be used with­out hav­ing to adapt or recom­pile the main pro­gram.
  • Exter­nal spec­i­fi­ca­tion of which pack­ages to load. This can be done by a con­fig­u­ra­tion file, path con­ven­tions, user input or oth­er mech­a­nisms for exter­nal spec­i­fi­ca­tion of code to be linked at run time.

 

We can derive a few design deci­sions from the con­straints above:

  • the plu­g­ins and the main pro­gram are to be in two sep­a­rate assem­blies
  • the main pro­gram will load the plu­g­ins dynam­i­cal­ly
  • the main pro­gram will rely on exter­nal spec­i­fi­ca­tion to deter­mine which plu­g­ins to load

So, I added two new projects to the solu­tion.

Style19_00

Style19Main will be the “main pro­gram” in this case, and the plu­g­ins will be defined in Style19Plugins.

First, we’ll define the con­tracts that these plu­g­ins must adhere to in Style19Main/PluginInterface.fs.

Style19_01

Notice that I have includ­ed a mark­er inter­face IPlu­g­in, we’ll see how this comes in handy lat­er on.

I have fol­lowed Crista’s exam­ple by split­ting up the term fre­quen­cy pro­gram into two stages — extract words from an input file, and find­ing the top 25 most fre­quent words.

Next, we’ll define the plu­g­ins that will imple­ment these con­tracts.

Style19_02

I have neglect­ed the sup­port­ing func­tions as they’re lift­ed right out of pre­vi­ous styles. The key point to take away here is that we have cre­at­ed two “plu­g­ins” that imple­ment the afore­men­tioned con­tracts.

Now we need to enable the “main pro­gram” to load these plu­g­ins dynam­i­cal­ly at run­time. Back in the Style19Main project, let’s take a peek at MainProgram.fs.

Here, we’ll define the con­fig­u­ra­tion that can be passed into the main pro­gram.

Style19_03

In the Con­fig type above, we declared a Plu­g­ins­Dir field which tells us where to load plu­g­ins from. In the real world, this might be by con­ven­tion — e.g. many pop­u­lar apps have a plu­g­ins fold­er in its root fold­er.

Giv­en an instance of this Con­fig type, we will do the fol­low­ing:


  1. find all .DLL files inside the plu­g­ins direc­to­ry
  2. dynam­i­cal­ly load them
  3. for each assem­bly, find all the pub­licly vis­i­ble types that:
    1. are con­crete, non-abstract types
    2. imple­ment the IPlu­g­in mark­er inter­face
  4. con­struct an instance of each type (there’s an implic­it assump­tion here that the type has a para­me­ter­less con­struc­tor)
  5. return the con­struct­ed plu­g­in as a map (keyed to the names of the assem­bly and type)

Style19_04

Once we have loaded all the avail­able plu­g­ins from DLLs in the spec­i­fied fold­er, we can pick out the ones we need based on the con­fig­u­ra­tion and exe­cute them in turn.

Style19_05

The last thing we need is the “exter­nal spec­i­fi­ca­tion”, which in this case will come from our Style19.fsx script file.

Here, we’ll sim­ply con­struct the con­fig­u­ra­tion and pass it along to the “main pro­gram”.

Style19_06

In this exam­ple I have includ­ed only one set of plu­g­ins, but the infra­struc­ture is in place for you to add sup­port for more plu­g­ins with­out hav­ing to recom­pile Style19Main (as per the con­straints).

I find the plu­g­ins style a very use­ful tool in your tool­box and have used it with rel­a­tive suc­cess in the past. Before you point it out to me, yes I’m aware of MEF, but I’m also not a fan of the amount of wiring required for it to work.

In gen­er­al I pre­fer the reflec­tion-based approach and find MEF intro­duces anoth­er lay­er of abstrac­tion that adds more com­plex­i­ty than it hides.

 

You can find the source code for this exer­cise here.