Skip to content

Archive

Tag: F*

This is quick tutorial showing how to get started with F* in Objective-C. F* is a proposed meta-pattern for application development (see here for related documents)

You can check out the sample project from SVN:

http://xp-dev.com/svn/harmony-framework/trunk/f-star/iOS/tutorial/FStarExample/

Setting up your project

  1. Open XCode.
  2. Go to File > New Project
  3. Select ‘Empty Application’.
  4. In Product Name, I input FStarExample.
  5. I put my projects under my experiments/ folder. XCode will create a folder for our project.

Arranging the sample project

I like to have ‘headroom’ in my projects. The initial project structure looks like this:

  • FStarExample (project node)
    • FStarExample (source location)
    • FStarExampleTests (for unit testing)
    • Frameworks (libraries required by the iOS SDK)
    • Products (build output)

I regroup all items, so that it looks like this:

  • FStarExample (project node)
    • source (custom group)
    • tests (previously, ‘FStarExampleTests’)
    • [implied]
      • FStarExample-Prefix.pch (digged up from ‘Supporting Files’ I just like to keep this one ‘visible’)
      • FStarExample (still the app delegate)
      • Products
      • Frameworks

Keep in mind that XCode groups are just a convenient way to… group files. For better and for worse, grouping is irrelevant to the underlying file structure.

Setting up an F* instance

Under source I create the following structure:

  • source
    • [m]
    • Facade.h
    • Dispatcher.h
    • Feature.h

The Facade will be used to instantiate all the features we use in our application. The Dispatcher provides the basis for gluing features together. Feature is the base class for all application features.

Note: XCode doesn’t encourage physical groupings (putting source files in separate folders – it is needless to say possible but doesn’t flow smoothly) so I’ll be content with XCode groups.

Creating a feature

To create a feature named foo, follow the steps:

  1. create a group named foo
  2. create a subclass of Feature, Foo inside group foo.
  3. foo overrides setup in Feature.
  4. import the feature class from Facade and add an entry to initFeatures.

The sample project illustrates the above steps.

Event handling

The dispatcher mediates communication between features via events. In the provided example:

  • Foo registers with the dispatcher to receive an event x. registration happens within the setup method. (While setup is running, nothing warrants that all features are already initialized so while it’s okay to register for events, it is not okay to dispatch.
  • After all features are setup, the start method is called on each feature, at which point Bar dispatches the event.
If you test the sample project (hold run button in top left until options appear), you can check the output in the log window.

Implementation notes

About event handling and the dispatcher
In my sample project, the dispatcher is not very scalable – as we go on and define more and more notifications, it will quickly become bloated. I will probably update the sample project a bit later to show how this can be fixed.
In practice this needs to be done with some care – when using F* defining new notifications is common enough that a templated approach quickly becomes desirable.
About the facade
In the facade each feature is bound to the applications in two ways:
  • We need to import the feature class
  • The feature class needs to be instantiated and added to the feature list.
Classes can be instantiated dynamically, which practically allows turning a feature off without having to change any code. Needless to say this comes at the cost of compile time safety.
The facade should be connected with FStarExampleAppDelegate. As the last call in application:didFinishLaunchingWithOptions: I instantiate the facade. A real application would need to provide access to UI resources (here, the window provided by the app delegate).

In Practice

When I use F*, I tend two follow a 3 step cycle.

1. induction

During the ‘inductive phase’, I try to focus exclusively on the problem I am solving. If I feel my feature requires external input (or a signal), I design notifications without worrying about the source of such notifications.

This approach becomes important over time because it allows solving the problem at hand without  burdening ourselves with the growing ‘mass of code’ that the project generates. For me this inductive phase is similar to ‘starting a new project’. Surely most programmers have noticed that they feel dynamic and fresh at the beginning of a new project?

2. testing

During the testing phase, notifications can be used to write acceptance tests – test the feature in isolation.

3. integration

During the integration phase, I make sure that the notifications the feature relies on are actually satisfied.

A few months ago, I put quite a bit of work into understanding an MVC framework called PureMVC and getting a team up to speed using it.

I didn’t like the PureMVC thing too much because it’s so heavy. In this post I described a strategy for doing MVC with your own, home baked MVC pattern instance.

Do you bake MVC? Here’s a way to get your three layer cake from the designer’s pencil to the forge.

Ideal means this stuff is paperwork. I haven’t really tried it – but frankly speaking it’s mostly a simplification of what goes on in PureMVC, with a little bit of salt. Bon Appetit

An ideal MVC instance

In this model, the software is structured around three broad categories instantiated as packages.

  • View classes. View classes use some GUI API such as flash, java swing, OpenGL or the like. View classes also hold view specific data(1). When the user performs an action, such as clicking a button, the view handles this action by firing an event (see below).
  • Model classes. Model classes hold your application data.
  • Controller commands. A controller command is a command that changes the way the application reacts to events from the model and view, rather than changing the view and model directly. Typically, a controller command does nothing except register callbacks causing such or such event to trigger such or such command. Additionally…
    • Model actions are commands that change the model in some way. These commands operate only on the model. When the model has changed in some way, and we want to update the UI, the model needs to fire an event
    • The UI is controlled by UI update commands – commands that change the UI in some way. These commands operate only on the UI.

More about events

In the above model, note that neither view or model ever handles an event directly. This is not to avoid type dependencies, but to avoid conceptual dependencies:

  • Maybe an animation just finished in the view. If the model handles this event, the model is doing something based on the fact that an animation is complete. Even though the actual view type is hidden from the model, the model now requires making a big assumption about the view to operate.
  • Maybe the model changed in such or such way. It may be that this causes just one change in the view, but it maybe that the view needs to update in many ways, or it may be that we are using a general purpose view component that is used in combination with concurrent models.

Also note that a view or model doesn’t directly instantiate commands. This is consistent with the fact that the view shouldn’t know about the model (and vice versa). There are many cases in which it may be convenient and safe to allow the view to know about the model; however, this is a simplification of the ideal instance described here, so to stick to the general rule, the view should generate a SEND_MAIL event, even though it is obvious that this event maps to the SendMail command.

Lightweight versus heavyweight events

When writing your application, you will often need to send quite a bit of data along with an event – for example if a view needs to update a table, you need to send whatever needs to be updated along with your event, not just a message.

In this case, you have two choices:

  1. Use untyped event data. This is in some ways unsafe (more parsing/relying on implicit data formats), but it saves the trouble of defining additional structs or classes to store event data.
  2. Use typed event data. It is commendable to include event definition in a subpackage, e.g model.event, view.event

More about the event dispatcher

To make the above work, you need a kind of event dispatcher that can broadcast events to receivers. All your view/model classes should be able to access the dispatcher so that they can trigger events. Your control commands should be able to access the dispatcher so they can register callbacks.

It may be a good idea to expose the dispatcher to the view/model and the controller commands using a different interface. We don’t want view classes and model classes to modify our event to command bindings accidentally

ViewManager and ModelManager

When a command is instantiated, it needs to get hold of the data that it wants to manipulate. A simple way to allow this is to define singleton ViewManager and ModelManager. So for example the ViewManager contains all the views, and ModelManager is the root of the model’s data hierarchy.

How does the application start?

You may have noticed that it’s the job of command to specify bindings from events to commands. This leads to one of those chicken and egg problems – which one started it all? an event or a command. This doesn’t really matter, but here are two solutions anyway:

  1. Use a Main command. Then your framework needs some kind of bootstrap class that you call start() on.
  2. Invoke a command to bootstrap your application.

How to use the above while enforcing feature driven separation

It may be a good idea to combine your MVC pattern with some degree of feature driven separation (I wrote a lot about the F* Meta-Pattern and feature driven separation somewhere else). Here’s how a simple, non adventurous way to do it:

  • Break down your controller package into subpackages – one package per feature.
  • Use a controller command to register all the callbacks and commands related to each given feature. This is the ‘feature root’
  • Bundle additional commands required to realize your feature in the same sub-package as the feature root.

There are pros and contras to the above approach, compared to a typical F* instance:

  • PRO 1: In many existing workflows, we use WYSIWYG UI builders to generate our views. The original F* pattern typically requires moving back UI building into the code, because you need to be able to add widgets as part of feature code – in F*, your ‘send mail’ feature will require you to add the send mail button to a toolbar – that’s the price to pay if you want the UI to be correct once you have the removed the mail feature.
    This is further reflected in the way UI designers work. In cool applications, the UI is typically very integrated. Removing a function often means redesign in this case.
  • PRO 2: In many applications, most features rely on a core data model – i.e most of the data is used by several features. F* fragments your data model across features and leaves the generation of the core model as a refactoring exercise. There are many cases in which designing the data model as a self contained entity, adding stuff to it over time, maybe more efficient.
  • CON: F* allows you to delete a feature by deleting all related classes. With the above strategy, deleting the feature deletes only application functionality, not widgets and model data related to such functionality.

In this article, I’m looking at two distinct things:

  • Basic use of AVAudioPlayer, a CocoaTouch class that gives reasonably good sound support for many applications. If you’re deep into waves, search OpenAL instead.
  • How to use the F* meta-pattern to integrate audio as a ‘pluggable’ feature, with minimum interaction with existing code.

As a side effect (this is integral to the spirit of the F* pattern), you might be able to integrate sample code from this article in your application very easily. If you’re not into the theory, please skip to the code and have fun.

A few days ago, I have decided to try applying the F* meta-pattern with my game integration. A first step towards this was i/o integration, for which I provided:

  • A dispatcher for in-game milestone events
  • A protocol used to identify consumers of milestone events.
  • Classes packaged separately that take care of i/o.

Practice makes better. I’m starting to understand how I can apply F* to developing my game, and audio integration is the next step.

Here, I want to associate a sound with specified actor activities. For now I’ll keep it simple, just start a sound when the actor begins an activity.

In my little game framework, the actor’s activity is updated at every frame. Activity changes only occur when the new activity is different from the previous activity. So what I’ll do is define an observer pattern allowing me to detect activity changes without creating type dependencies with the Actor class. I’ll then register an instance of a ‘sound feature class’ with the event dispatcher.

Observer Pattern

I have moved my dispatchers and listeners to a separate package. I then create a new group including the following:

  • ActivityChangeDispatcher provides static methods used to notify listeners to activity change events.
  • ActivityChangeListener specifies… activity change listener functions.

Here is the code for ActivityChangeListener (act is just a typedef in Definitions.h):

#import "Definitions.h"
@class Actor;
@protocol ActivityChangeListener
-(void)actor:(Actor*)a activityChangedFrom:(act)a0 to:(act)a1;
@end

Then the header file for ActivityChangeDispatcher

#import "Definitions.h"
#import "ActivityChangeListener.h"
#import "EventDispatcherMacros.h"
@class Actor;
@interface ActivityChangeDispatcher : NSObject
DEC_LISTENERS(ActivityChangeListener)
+(void)actor:(Actor*)a activityChangedFrom:(act)a0 to:(act)a1;
@end

Finally the implementation of ActivityChangeDispatcher.

#import "ActivityChangeDispatcher.h"
@implementation ActivityChangeDispatcher
DEF_LISTENERS(ActivityChangeListener)
+(void)actor:(Actor*)a activityChangedFrom:(act)a0 to:(act)a1{
	for (id l in listeners) {
		[l actor:a activityChangedFrom:a0 to:a1];
	}
}
@end

DEC_LISTENER() and DEF_LISTENER() are declared as follows in EventDispatcherMacros.h:

#define DEF_LISTENERS(interface)								\
static NSMutableArray* listeners;								\
+(void)addListener:(id)l{							\
	if (listeners==nil)listeners=[[NSMutableArray alloc]init];		\
		[listeners addObject:l];					\
}														\
+(void)removeListener:(id)l{							\
	if (listeners==nil)return;						\
	[listeners removeObject:l];						\
}																

#define DEC_LISTENERS(interface)						\
+(void)addListener:(id)l;							\
+(void)removeListener:(id)l;

I was kind of hoping I might be able to define a catch-all macro avoiding writing code for new observer pattern classes altogether. This isn’t quite like it although some of the boiler plate has been removed. But then I can probably do better. Next time i’ll try to make this more concise as I do not want to define more than one event per dispatcher.

Once this is done, I need to activate the dispatch by pasting this code wherever activity changes:

[ActivityChangeDispatcher actor:a activityChangedFrom:x to:y];

The Sound Feature

My sound feature will be PlaySoundOnActivityChange.

Here’s the header

#import "ActivityChangeListener.h"
#import "Definitions.h"

@interface PlaySoundOnActivityChange : NSObject  {
	NSMutableDictionary* map;
	NSMutableDictionary* players;
}
-(void)map:(tag)label to:(tag)soundFile;
@end

Now here’s the implementation:

#import "PlaySoundOnActivityChange.h"
#import <AVFoundation/AVFoundation.h>
#import "ActivityChangeDispatcher.h"

@interface PlaySoundOnActivityChange (private)
-(AVAudioPlayer*)prepareSound:(tag)name;
@end

@implementation PlaySoundOnActivityChange
-(id)init{
	if (self=[super init]) {
		map=[[NSMutableDictionary alloc]init];
		players=[[NSMutableDictionary alloc]init];
		[ActivityChangeDispatcher addListener:self];
	}return self;
}
-(void)actor:(Actor *)a activityChangedFrom:(act)a0 to:(act)a1{
	NSString* code=[NSString stringWithFormat:@"%i",a1];
	tag name=[map objectForKey:code];
	if (name==nil)return;
	AVAudioPlayer* player=[players objectForKey:name];
	if (player==nil)player=[self prepareSound:name];
	if (player==nil)return;
	[player play];
}

-(void)map:(tag)label to:(tag)soundFile{
	[map setObject:soundFile forKey:label];
}
// private ---------------------------------------------------------
-(AVAudioPlayer*)prepareSound:(tag)name{
	NSBundle	*mainBundle = [NSBundle mainBundle];
	NSError		*error;
	NSURL		*url = [NSURL fileURLWithPath:
[mainBundle pathForResource:name ofType:@"caf"]];
	//
	AVAudioPlayer* player = [[AVAudioPlayer alloc]
initWithContentsOfURL:url error:&error];
	if (!player) {
		NSLog(@"no player: %@", [error localizedDescription]);
		return nil;
	}
	[player prepareToPlay];
	[players setObject:player forKey:name];
	return player;
}
@end

I haven’t tested this yet (OK, it compiles… – PS: after testing, it actually works). Also, I’m a little bit worried about what will happen if I have a 100 or a 1000 sounds registered in my map – I don’t really know what resources players are holding to, especially once [prepareToPlay] has been invoked. Pending, note the way a feature is designed and implemented using F*, and also a couple of errors:

  • I have provided an event dispatcher allowing me to trigger sounds at the right moment. The generated events provide me with all the information I need to select sound.
  • Actor does not import PlaySoundOnActivityChange – I can remove the sound feature without breaking my code (so far).
  • PlaySoundOnActivityChange and ActivityChangeListener/Dispatcher import the actor class. This could, and should, be easily avoided (so that I can use the same sound feature for non actor sounds, or in an altogether different context.

I need to import sounds into my project and map them. Probably this would be done better with a property list defined as a resource but for now, I just want to map a couple of values and check the result. So I define a feature setup class with a couple of mappings. Invoke the setup class directly from my GameSetup class. Here’s the code for InitSound:

#import "InitSound.h"
#import "PlaySoundOnActivityChange.h"
#import "Definitions.h"
#import "VRConstants.h"

@implementation InitSound
+(void)apply{
	NSString* label1=[NSString stringWithFormat:@"%i",WALK];
	NSString* label2=[NSString stringWithFormat:@"%i",STAB];
	PlaySoundOnActivityChange* feature=[[PlaySoundOnActivityChange alloc]init];
	[feature map:label1 to:@"tick"];  // just reused the sounds
	[feature map:label2 to:@"tock"]; // from Metronome example for a try.
}
@end

Done with sound.

How far can we go into the writing of an application without losing momentum?

As an application becomes richer, it typically becomes more complex. There are two aspects to this:

  • Logical complexity increases as a result of new functionality binding to existing functionality.
  • Physical complexity increases as a result of increased application size.

The result of increasing complexity is that developers find it increasingly challenging to add new features to an application. This complexity manifests itself in two ways:

  1. Your workspace is increasingly cluttered with irrelevant entities – entities that do not contribute to the feature you are developing
  2. Your code is increasingly dependant on existing elements of functionality.

Very large applications are typically broken down into modules. Modules reduce logical complexity by providing narrowing interfaces; physical complexity is reduced by keeping all the code for a given module separate from the code for other modules.

Feature architectures enforce micro-modularity:

  • Each feature typically maps a single value increment or story.
  • Features are abstracted from each other using channeled notifications.

Why design feature architectures?

Feature architectures are derived from story driven development. In agile, story driven development, developers collaborate towards the development of a given story cutting across the layers of a selected framework. Agile ensures that developers deliver value faster; in suitable contexts (assuming that value can be delivered incrementally), this maximises profitability while reducing the risk to produce large amounts of unused functionality.

Feature architectures take advantage of the separation between source spaces and runtime spaces to allow developing well formed (layered) runtime architectures while limiting linearly growing physical and logical complexity. This allows developers to focus on delivering atomic value increments without incurring mounting familiarization costs and splitting development across framework layers. Also, this may allow fairly large developer teams to collaborate without facing large communication costs.

In a nutshell, feature architectures and micro-modularity target value driven separation. In contrast, existing separation strategies and meta-patterns provide abstract roles that can be re-instantiated in a variety of contexts (the MVC meta-pattern is an example). This affords industry endorsement for high level design solutions. Beyond, classic separation models emphasize a component drive approach. While this means that component cores can be developed in parallel, this may result in glossing over communication interfaces between components.

Layered, component oriented architectures emphasize potential and structural integrity and are more suitable for long term investments and library development. Feature architectures emphasize value increments and may be ideal for writing application front ends:

  • Reusable data and view components are already available
  • Applications are feature rich, yet do not involve much complex code.
  • Value increments typically involve cutting across layered architectures.

Working using a feature architecture

Feature architectures require developers to package features separately. Each feature is developed as an integrated micro-module – if you are writing an application, a feature will probably bundle view, model and controller functionality.

  1. Each developer or pair are responsible for designing and implementing a feature derived from a user story.
  2. The core functionality involved in developing a feature is developed independently from other features.
  3. The source code related to a given feature is bundled within a separate package.
  4. Developers integrate their feature by implementing handlers for channeled notifications.
  5. Developers expose required resources by providing external notifications.
  6. All source code for a given feature may be removed without causing the application to break at compile time.

Mmh… this may not be the best introduction to feature architectures I could write. I’ll try to improve on that and will also provide an application template very soon.

In agile development, the emphasis is on delivering business value incrementally. While this explicits features within software cycle management, it may also reduce the developers’ visibility over software architecture; this may part in explain why agile methods rely heavily on refactoring.

In contrast, classic development approaches emphasize planning and upfront design. While this may promote architectural integrity, it has been largely demonstrated that  this reduces both customer satisfaction and software profitability, typically resulting in a large amount of unused functionality.
When debugging and processing change requests, development teams naturally adopt a feature oriented approach; this is because bugs and change requests are normally raised by end users.

Overall, software features, whether implicit or explicit to the development cycle as agile stories or bugs, remain largely implicit to code-bases because the high level structure of software applications follows a technical orientation – a paramount example in front end development being the unavoidable Model View Controller meta-pattern. Features typically cut across layered architectures. For agile programmers and maintenance teams, completing stories, fixing bugs and resolving change requests requires juggling between application layers. For traditional teams, reduced visibility over application features will result in a higher number of bugs and decreased customer satisfaction.

I define Feature Programming as a set of methods designed to reduce the cost of software development by revealing features within source code. This is mainly achieved in two ways:

  • Feature Annotations using domain semantics allow software architects to retain a layered architecture while increasing feature visibility.
  • Feature Architectures emphasize feature separation over layering when deciding the high level structure of an application.

Both approaches are expected to reduce costs in front end development. While feature annotations provides non invasive, lightweight techniques for teams that want to reduce liabilities in the software cycle, Feature architectures may be very suitable for decentralized and open source application projects and further afford the creation of reusable value increments.

In this article, I cover feature annotations; later I will cover feature architectures in another article and will also demonstrate a small framework guiding the development of feature centric front ends while affording elements of layering using a suitable interpretation of the MVC meta-pattern – stay posted!

Feature Annotations

Annotating features simply requires a fairly disciplined approach to tracing code. Feature annotations can be applied either during development or retrospectively, as a scaffolding technique used to reduce the cost of maintaining legacy code.

Feature annotations requires the following:

  1. One or several feature classes define static functions expressing application features. Such feature traces must be easy to spot and suitable for automated discovery (in Java or ActionScript, you may use $ as a prefix).
  2. Each function in a feature class encapsulates a trace statement. Traces may be logged, output to a console or even displayed in an application specific debug window.
  3. Semantics for feature functions are typically provided either as story names or itemized requirements.
  4. All code that relates to a specific feature must be annotated using trace function invocations (‘trace annotations’). Within an agile process, a developer writing a story will tag their code, and otherwise contributing code, using trace annotations (for each class involved in the developed story, at least one function or constructor should be annotated).
  5. It is important that feature traces can be filtered easily either at runtime or at compile time (commenting out unwanted traces may be sufficient in simple cases).

Feature annotations constitute a non invasive technique. Adding feature annotations cannot break existing code and doesn’t require re-thinking your approach to software architecture. Developers working on agile stories need not be concerned with selecting trace annotations as the same annotation semantics are used over and over during story development.

In spite of this simplicity, feature modeling provides several benefits that will help in drastically increasing a development team’s reactivity to change requests while increasing software maintainability:

  1. If you are doing code reviews, feature annotations reveal and delimit the code related to a given feature and afford validating the code from the point of effectiveness. Classical code reviews validate structural integrity, glossing over functionality scattered across application layers.
  2. New starters observe feature traces at runtime and isolate features in the source; this complements a developer’s understanding of packaging and structure; developers also appreciate the software’s actual uses by reviewing feature classes. This results in decreased learning curves and reinforced consistency across the code-base given multiplied reuse opportunities.
  3. Maintenance teams benefit orderly, channeled traces mapping business logic using domain language. Observing channeled traces and isolating defective features can save hours spent learning control flow and discovering all scattered elements potentially involved in a defect.
  4. Liabilities associated with reuse are reduced given that concurrent, co-located traces provide tell-tale signs of reuse. A developer targeting a given code fragment will be able to determine which elements of functionality are affected, thereby correctly assessing the risk involved in modifying shared resources and identifying functionality that may require regression testing.