Berichten met label development

A Short Argument for Declarative Transformation Development

(cross-posted from blog.conceptworks.eu)

Due to increasing use of domain specific languages (DSLs), declarative style of modeling is quetly spreading among users of MDE tools. Indeed, it is easy to find examples of declarative DSLs, e.g. at DSM Forums or this blog. There is however a group of users, among which the delarative style of modeling has not managed to spread – transformation developers. I am not sure if it has something todo with the group itself or with the fact that the majority of today’s transformation definition languages (TDL) are still more imperative in style (I am aware of QVT Relations and ATL, but these are rather exceptions than the norm).

There are quite a few good reasons why one would consider using a declarative language for transformation definition: reduction of information content in transformation definitions (and hence higher productivity of transformation developers), more agile DSL evolution, transformation definitions as models, higher compatibility with parallel computing, etc..

Today I would like to share some practical results that illustrate reduction of information content due to use of a declarative language.

CHART vs. Java

The following examples are kindly provided by Maarten de Mol and Arend Rensink from University of Twente. In CHARTER project, they are working on certifiable transformation technology for development of safety-critical embedded systems.

Before proceeding to the examples, here are a few relevant highlights of their technology:

  • Partially declarative transformation definition language (CHART): based on graph transformation and intended to be useable by Java programmers.
  • Transformation compiler (RDT): given a transformation definition written in CHART, generates its executable implementation in Java. The produced code runs against and transforms user provided data.

Figures in rows of the gallery present transformation rules findRich and addPicture respectively. Figures in columns show these rules written in CHART and Java. The important Java methods are match() and update(), which are the translations of the similarly named blocks in the CHARTER rules.

Figures 1-4: Rules findRich and addPicture written in CHART and Java

In Figure 1, a match block counts 10 LOC against 41 LOC in Java (Figure 2), which constitutes a reduction of information content by 75%. In Figure 3, an update block counts 12 LOC against 65 LOC in Java, an 80% reduction.

Both examples show significant reduction of information content in CHART rules. The reduction is even stronger if one takes into account that Java implementations also have to address technical concerns, which do not exist in CHART rules. In this case reduction is 92% (13 LOC vs. 160 LOC for rule findRich).

In experence of another CHARTER partner, who evaluates CHART/RDT in practice, a CHART transformation definition counted 1024 lines of code against 8000 in Java, an 87% reduction of information content [1]. Author’s own industrial experiences elsewhere with AToM3 GG rules (declarative) and QVT Operational (imperative) agree with the above results as well.

While exact reduction numbers are certainly arguable, the overal trend in the above experiences is that use of a declarative TDL can result in dramatic reduction of information content and manyfold increase of development productivity.

Conclusion

Despite industrial successes of MDE (which are often hidden), it is my experience that model-driven methods have hard times keeping up as organizations evolve. One factor behing this lag is slow speed of transformation development. Practical industrial experiences such as above, show that declarative languages have potential to significantly improve agility of transformation development.

What are your experiences with declarative TDLs and agile language development? Can you share concrete examples or provide references to declarative TDLs?

References

[1] de Mol, M.J. and Rensink, A. and Hunt, J.J. (2012) Graph Transforming Java Data. In: Proceedings of the 15th International Conference on Fundamental Approaches to Software Engineering (FASE 2012), 26-29 Mar 2012, Talinn, Estonia. Lecture Notes in Computer Science. Springer Verlag.

, , , , , , , , , , , , , ,

Nog geen reacties

CHARTER Surveillance Use Case – Industrial Evaluation

Screen shot 2011-10-10 at 4.18.26 PMThis month, Luminis has started development of a surveillance use case. The purpose of the case is industrial assessment and validation of tools and technologies developed in the “Critical and High Assurance Requirements Transformed through Engineering Rigor” project (CHARTER).  The ultimate goal of CHARTER is to ease, accelerate, and cost-reduce the certification of embedded systems. The CHARTER tool-suite employs real-time Java, Model Driven Development (MDD), rule-based compilation and formal verification. The coming series of articles will describe evaluation experiences in the surveillance use case.

The CHARTER project includes user partners from four key industries: aerospace, automotive, surveillance and medical, each of which develops embedded systems that require high assurance or formal certification in order to meet business or governmental requirements. The four user partners will each validate the CHARTER tools and methodology using industrial applications and actual development scenarios, which will provide feedback for the project and ensure the tools and technologies perform as expected, and deliver the expected improvements in embedded systems development. As part of the evaluation process, metrics will be used to quantify industrial experiences in terms of development effort, cost savings, verification time, etc., to document for others the benefits achieved.

The CHARTER project was established to improve the software development process for developing critical embedded systems. Critical embedded software systems assist, accelerate, and control various aspects of society and are common in cars, aircraft, medical instruments and major industrial and utility plants. These systems are critical to human life and need to be held to the highest standards of performance through formal certification procedures. Improving the quality and robustness of these systems is paramount to their widespread adoption.

, , , , , , , , , ,

Nog geen reacties

Measuring Quailty of a Metamodel

L from pencilsNowadays DSLs seem to be everywhere. If 5 years ago DSL was an exotic word in the UML dominated model driven world, today it has established a strong following. A recent research on how MDE is used in industry [1], indicated that nearly 40% of respondents use in-house DSLs (alongside of other languages). The in-house qualifier is important, as these DSLs are very likely to be developed with metamodels. In such cases, a quality benchmark may help language development. Yet, it is not easy to find such a benchmark, let alone one that is widely accepted.

Five levels of Metamodelling

One quality benchmark that I found useful is described by Tony Clarks et. al in [2]. The authors define 5 levels of quality. These briefly are:

  1. The lowest level: a simple abstract syntax is defined, but not implemented yet in a tool. The static and dynamic semantics of the language is informal and incomplete. There is no specific tool support: an existing language is repurposed, compliance with the DSL is manually maintained and models are mostly interpreted by users.
  2. At this level, the abstract syntax and static semantics have been largely defined, implemented in a tool and validated. The dynamic semantics is still informally defined.
  3. The abstract syntax is completely implemented and tested. Concrete syntax has been defined for the language, but not implemented yet. Optimization of the language architecture has started.
  4. The concrete syntax of the language has been implemented and tested. Users create models either visually and textually. The language architecture has been optimized for reuse and extensibility. Tool support for dynamic semantics begins to appear.
  5. The topmost level: all aspects of the language have been modeled, including its semantics. Models written in the language can be processed by the tool. Examples thereof include code generation, execution, simulation, verification. The language architecture is well optimized for reuse.

While the original intention of the benchmark was to assess metamodels, I found it also useful for estimating metamodelling capabilities of MD tools. If a tool is not capable of supporting development needs for a certain level, then that level will be the quality ceiling for all metamodels defined with the tool. In my experience, DSLs in traditional (fixed method) CASE Tools do not achieve level greater than 1. Metamodels in UML tools often do not reach level 4 (and often lack static semantics and concrete syntax). Language workbenches can typically produce level 5 metamodels.

Conclusion

The referenced benchmark provides a first order approximation of quality of metamodels. Furthermore, these 5 levels provide those looking for MDx technology, with a simple framework at least to question the marketing information by tool vendors. In my opinion this benchmark may be a useful ingredient in an answer to a more general question of comparing MD technologies.

What are your experiences with measuring quality of metamodels or comparing metamodelling capabilities of MDx tools? Which aspects are you interested in and how do you measure them?

References

[1] John Hutchinson, Mark Rouncefield, Jon Whittle, and Steinar Kristoffersen. Empirical Assessment of MDE in Industry. ICSE 2011.

[2] Tony Clark, Andy Evans, Paul Sammut, and James Willans. Applied Metamodelling: A foundation for Language Driven Development. Version 0.1. Xactium Ltd., 2004.

Image by Aqua

, , , , , , ,

Nog geen reacties

How to build a custom model interpreter in a model-driven way

Blogs by Johan den Haan, Stefano Butti and Jordi Cabot raised interesting discussions about code generation (CG) and model interpretation (MI). One observation I took from these discussions is that MI is still little known. Previously I demonstrated operation of a custom-made model interpreter for a so-called weighbridge domain. Today I would like to share my experience of building this interpreter in a model-driven way.

MDE Approach

Two main choices underpin the process and technology used to develop the interpreter:

  1. Execution semantics of the interpreter is defined within the problem domain itself (weighbridge in this case), without translating it to another domain (e.g. .Net or Java) as it is the case with CG. Such definition of semantics is also known as operational semantics. The advantage is reduction of development complexity: out of at least 2 domains needed for CG, only one and the more abstract domain is sufficient.
  2. Operational semantics is defined within an MDE framework. This enables customization of modeling language for problem domains besides that of the weighbridge example. Moreover, transformation capabilities are used to define operational semantics.

Domain-specific, nested interpretation MDE framework

Figure 1: Domain-specific, nested interpretation (DSNI) MDE framework

Figure 1 shows the MDA framework [1] after it has been adapted to reflect the above mentioned choices. (If you are confused between MDA and MDE, you might find this article useful.) In contrast to MDA, there is no PIM or PSM model, but single computational independent model (CIM) written in DSL. CIM is both source and target of Transformation Tool. Transformation Tool carries out transformation classified as same language, same model. Transformation Definition defines operational semantics. It is not important if Transformation Definition Language (TDL), extends the Metalanguage as in MDA or is customizable by means of meta-specification. Therefore TDL is omitted from the framework and TDL selection criteria are defined instead (see below). Finally, new concept System Context is connected to Transformation Tool. This is due to the fact that interpretation as system exhibits external behavior through communication with other systems.

This approach can be described as nested interpretation, where a domain-specific interpreter is executed (nested) by a generic interpreter. From this perspective, Transformation Tool assumes the role of a generic interpreter and execution of Transformation Definition fills in the role of the domain-specific interpreter.

TDL Selection Criteria

Selection criteria for transformation definition language are:

  • declarative modeling
  • support for domain-specific notation

These criteria help to reduce development complexity and improve communication with problem domain experts.

Selected MD Technology

AToM3 is a free language workbench written in Python and under development at the Modelling, Simulation and Design Lab (MSDL) in the School of Computer Science of McGill University. The workbench closely matches the DSNI framework and meets the TDL selection criteria.

In AToM3, DSLs and models are described as graphs. From a language specification written in the metalanguage (ER formalism), AToM3 generates a tool to visually manipulate (create and edit) models written in the specified DSL. Model transformations are performed by graph rewriting. The transformations themselves can thus be declaratively expressed as graph-grammar models. However, AToM3 provides no communication infrastructure as needed by the framework.

Proof of Concept

As demonstration, a language specification for the weighbridge domain was defined (see sections domain and weighbridge DSL here) and graph rewriting was used to model operational semantics (see below). Source code of AToM3 itself, being written in Python, was extended to support web services for the communication purpose.

Operational Semantics

As blueprint for operational semantics of the interpreter, we took πDemos [2], a small process-oriented discrete event simulation language. There is a number of πDemos events that change state of a weighbridge system. For each such event, [2] defined the transitions induced on system state. While the original used functional programming language, this work uses graph rewriting and a graph grammar (GG) rule is defined per event.

Priority GG Rule Description
50 importProcess Adds an external vehicle to EL
25 removeProcess Deletes a vehicle that has completed its todos (events)
40 newR Creates a new weighbridge
40 decP Creates a new vehicle class
40 newP Creates a vehicle from a vehicle class
40 getR Acquires a non-busy weighbridge
40 blockProcess Blocks a vehicle acquiring a busy weighbridge
40 promoteProcess Unblocks a delayed vehicle
40 useR Moves a vehicle on a weighbridge until service is complete
30 releaseResource Moves a served vehicle from a weighbridge to EL
41 putR Releases an occupied weighbridge

Table 1: Graph grammar rules of weighbridge events

Table 1 lists the minimum set of required events and their corresponding GG rules. Execution of such rules needs to be globally orchestrated through proper sequencing. The rules, together with execution sequencing, form an operational semantics model of the interpreter.

For complete description of the model, please refer to [3]. In the following, we present a detailed description of an example rule, followed by the execution sequencing model.

Example GG Rule

LHS

a) Left-hand side (LHS)

RHS

b) Right-hand side (RHS)

Figure 2: Subgraphs of the promoteProcess rule

Rule promoteProcess releases a busy weighbridge (bluish box in Figure 2a) that delays at least one vehicle (yellow box labelled 5). In the new state, the weighbridge remains busy and the blocked vehicle (5) is removed from the head of queue Delay and inserted in waiting queue EL.

The rule is executed if:

  1. The left-hand side (LHS) shown in Figure 2a is matched in the host graph (the CIM model).
  2. Associated condition is true: the weighbridge in LHS is the one referred to in the imminent event putR (a todo) in the body (a todo list) of the first vehicle (label 21) in queue EL.

If the above holds, the matched part of the CIM model is substituted with the right-hand side (RHS) shown in Figure 2b. Note new objects are labelled 10, 11, 13. The entities and relationships in RHS are initialized as follows:

  1. Objects copied from LHS keep all their properties.
  2. Imminent event putR (a todo)  of the current vehicle (21) is completed.
  3. All properties of blocked vehicle (5) are copied to vehicle (10).

Execution Sequencing

The execution sequencing is based on the next-event approach: Next event to execute is always the imminent event in the body of the current vehicle. Informally, the operational semantics of execution sequencing is as follows: if EL is empty, interpreter idles until at least one vehicle is inserted in EL. Such vehicle becomes current. If the body of the current vehicle is empty then it is removed from EL and EL is examined again. Otherwise, interpreter  executes a GG rule corresponding to the imminent event of the current and EL is examined again. Note that whenever interpreter is idle, EL is being updated with new vehicles that meanwhile might have arrived from system context.

The execution sequencing is implemented by organizing GG rules into groups, each group having its own base priority. These groups, in the descending order of priority are: vehicle removal, weighing activities and vehicle arrival. Within a group, each rule is assigned a relative priority. If pattern matching of two and more rules within a group is deterministic on the basis of LHSs and conditions, then these rules can share the same priority level. Example rule priorities are given in Table 1.

Conclusion

The demonstrated development approach is characterized by a very high level of abstraction, direct involvement of problem domain experts and absence of software development. All these factors contribute to fast development times: The lead time of this one man project including research and development was 3 weeks. Admittedly, tests of the produced model interpreter showed noticeable performance penalty due to 1) repurposing of MD technology that was not designed for use as interpreter and 2) the overhead introduced by nested interpretation. In my opinion there is much room for performance improvement and I am wondering if MDE can prove useful again. An important message from this experience is that model interpretation does not have to be prerogative of big commercial tools and can get closer to code generation in terms of accessibility.

References

[1] Anneke G. Kleppe, Jos Warmer and Wim Bast. “MDA Explained: The Model Driven Architecture: Practice and Promise”. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, April 2003.

[2] Graham Birtwistle and Chris Tofts. “An operational semantics of process-oriented simulation languages: Part 1 πDemos”. ACM Transactions on Modeling and Com- puter Simulation, 10(4):299–333, December 1994.

[3] Andriy Levytskyy. “Model Driven Construction and Customization of Modeling & Simulation Web Applications”. PhD thesis, Delft University of Technology, Delft, The Netherlands, January 2009.

, , , , , , , , , , , , , , ,

1 reactie

Howto use MvcContrib.Pagination with a ViewModel

Howto use MvcContrib.Pagination with a ViewModel

I was looking for a simple solution for paging in a ASP.MVC2 project. Though we already use MVCContrib.Grid this was the first place I searched.

The solution MVCContrib offers is elegant in the way that they separated the concept of paging, a ui element to show paging (next , previous, …) and a grid to show the content. This separation allows us to using paging on a custom table based page as well.

Because we are using ViewModels a little extra effort was needed to retain the paging information from the domain layer in the view layer.

Step by step:

* Get a Querable from repository using Domain Objects

IQueryable<DomainObject> domainObjects = rep.GetAll()

* Filter and Sort using Linq

domainObjects = domainObjects.Where(...).OrderBy(...)

* Get the data

IPagination<DomainObject> pageOfData domainObjects.AsPagination( page, pageSize)

* Map to the ViewModel

IPagination<DomainObjectViewModel> model = DomainObjectViewModel.Map( pageOfData )

* Mapping needs to retain the page information from the domain

var list = new List<DomainObjectViewModel>();
foreach (var domainObject in domainObjects)
{
list.Add(Map(domainObject));
}
new CustomPagination<DomainObjectViewModel>(list.AsEnumerable(),
    pageOfData.PageNumber, 
    pageOfData.PageSize, 
    pageOfData.TotalItems);

* Show the information on a page in a grid

<%= Html.Grid(Model).AutoGenerateColumns() %>

* Show the pager

<%= Html.Pager(Model).First( "<<").Last(">>").Next(">").Previous("<")
.Format( "Item {0} - {1} van {2} ") %>

Step by step:The ====# HowTo====
* Get a Querable from repository using Domain Objects
@@IQueryable<DomainObject> domainObjects = rep.GetAll()@@
* Filter and Sort using Linq
@@domainObjects = domainObjects.Where(…).OrderBy(…)@@
* Get the data
@@IPagination<DomainObject> pageOfData domainObjects.AsPagination( page, pageSize)@@
* Map to the ViewModel
@@IPagination<DomainObjectViewModel> model = DomainObjectViewModel.Map( pageOfData )@@
* Mapping needs to retain the page information from the domain
@@var list = new List<DomainObjectViewModel>();
foreach (var domainObject in domainObjects)
{
list.Add(Map(domainObject));
}
new CustomPagination<DomainObjectViewModel>(list.AsEnumerable(), pageOfData.PageNumber, pageOfData.PageSize, pageOfData.TotalItems);
@@
* Show the information on a page in a grid
* Show the pager

, , ,

Nog geen reacties

DSL Design Tutorial at PPL2010

Photo from Inception (2010)In MDD explicit knowledge of the domain is crucial for successful development of domain-specific modeling languages (DSML). Yet many starting DSL developers are missing the skill of domain knowledge conceptualization. The main symptoms are difficulty to come up with good language concepts and struggling with levels of abstractions.

While language design remains an art, there are a number of paradigms, techniques and guidelines that can make creation of DSLs easier. These helping means are the core of the DSL design tutorial developed at Luminis Software Development.

The tutorial was given for the first time during the PPL2010 conference that took place on November 17 & 18 at Océ R&D, Venlo, NL. A small group of participants learned basics of domain analysis, participated in domain definition and implemented a simple metamodel of their own. The general feedback was very positive.

The slides for the tutorial can be downloaded here from the Bits&Chips website.

, , , , , ,

Nog geen reacties

Detachering versus inhouse

Detachering versus inhouse


Enkele maanden geleden is het project waarop ik gedetacheerd zat afgerond; inmiddels heb ik ook een paar maanden meegedaan aan een inhouse-project. Bij Luminis doen we weinig aan pure detachering, vandaar dat het me leuk lijkt om ‘ns te bekijken wat de overeenkomsten en verschillen met onze andere projecten zijn.

Een deel van onze projecten vindt plaats in onze eigen kantoren, een ander deel bij klanten, waar dan een aantal mensen van de klant en een aantal mensen van Luminis samenwerken.

Sfeer

De sfeer tijdens het werken aan het project hangt vooral af van het team waarin je werkt, en ook een beetje van de lokatie. Uiteraard heb je daar zelf enorm veel invloed op, je kunt een goede dosis humor meenemen, meegaan met de lunchwandeling, eens koffie voor een ander meenemen, et cetera.

Inhoudelijk is het lastig onderscheid maken. Natuurlijk richt Luminis zich op leuke, innovatieve projecten, maar dat was het project waarop ik gedetacheerd zat ook. Bij sommige projecten kun je van tevoren al redelijk inschatten dat het een saaie klus gaat worden, dat kun je dan ook aangeven bij je baas, met een beetje geluk mag een ander het doen :-) .

Bij inhouse-projecten ken je meestal je collega’s al, wat de start van een project misschien wat makkelijker maakt, zeker als je wat introverter bent (da’s tenminste mijn ervaring). Misschien heb je ook wel meer overeenkomsten met directe collega’s, omdat ze bij hetzelfde bedrijf werken en dus dezelfde visie delen.

Wanneer je gedetacheerd wordt ben je aan het begin naast de inhoudelijke kant van een project ook tijd kwijt aan het leren kennen van het team, de sfeer en de praktische zaken van het bedrijf waar je aan de slag gaat.

Overhead

Het verdwijnen van de overhead van gedetacheerd zijn is fijn, geen dubbele urenregistratie (met de onvermijdelijke verschillen, en het bijbehorende uitzoekwerk), geen extra e-mailaccount en kalendersynchronisatie.

Ook is de organisatiestructuur al bekend voor je, zodat je direct naar de juiste persoon kunt stappen om iets geregeld te krijgen.

M’n vorige project was bij een overheidsorganisatie, waar internet op de werkplek nu pas werkelijkheid aan het worden is. Het is een verademing dat dat bij Luminis anders is: een snelle internetverbinding, alle tooling (issue tracker, wiki, versiebeheer) is (intern en extern) bereikbaar. ‘t Is natuurlijk niet zo dat je nooit internet hebt op inhuurplekken, maar het komt vaker voor.

Et tu?

Wat zijn jouw ervaringen? Heb je bewust gekozen voor een detacheerder of juist een bedrijf dat zelf projecten doet? Was het een goede keus of zou je het een volgende keer anders doen?

, , , , , ,

1 reactie

Building iPhone applications using MonoTouch, part 5: software design considerations

In the previous 4 posts (4, 3, 2, 1) I gave a lot of attention to the overall structure of an iPhone application. In this post I want to talk about a topic that is more concerned with general software design issues: where do I do what?

Get SOLID

There are two things that I always try to keep in mind when making software: loose coupling and tight cohesion. In other words:

  1. make sure your classes don’t know things that they don’t need to know
  2. make sure your classes are good at one thing and one thing only

These guidelines are part of the 5 SOLID principles of class design by Uncle Bob Martin:

  1. Single Responsibility Principle
  2. Open Closed Principle
  3. Liskov Substitution Principle
  4. Interface Segregation Principle
  5. Dependency Inversion Principle

This stuff is really interesting and sort-of scientific, but it is also like making your database comply to the 5-th Normal Form: nobody does that. You’re happy with a 3rd Normal Form database. So I’m happy when I see code that complies with at least two of the above principles.

So what I do when building my iPhone apps is constantly asking myself: should this code be in this place? Sometimes I know right away that the answer is No, but still leave it there until I have a better idea on where to put it then. And with the (for me) rather unusual structure that the Cocao Framework forces upon me, it may take some time before I eventually find out where to put it.

Let me give you an example.

When you want to load data into a UITableView you must create a UITableViewDataSource and assign that to the DataSource property of your UITableViewController. Like this:

public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
	tableView.DataSource = new LeesPlankjeDataSource();
	window.AddSubview (tableView);
	window.MakeKeyAndVisible ();
	return true;
}

The class instantiated at line 3 is something like this:

public class LeesPlankjeDataSource : UITableViewDataSource
{
	private string[] woordjes = new string[] {"aap", "noot", "mies"};
	public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
	{
		UITableViewCell cell = tableView.DequeueReusableCell("plankje");
		if (cell == null)
		{
			cell = new UITableViewCell(UITableViewCellStyle.Default, "plankje");
		}
		cell.TextLabel.Text = woordjes[indexPath.Row];
		return cell;
	}
 
	public override int RowsInSection (UITableView tableview, int section)
	{
		return woordjes.Length;
	}
}

On line 3 you see the actual “data store”, and on line 4 an important override. This method gets called by Cocoa when loading data in your UITableView. So the controller has a data source and the appropriate methods get called by the framework.

On to a more realistic implementation

If I want to advance my class a bit, I could imagine that the data is not a fixed array of strings, but gets passed in at construction time. Maybe I read from a file or from a URL.

That changes my class to this:

public class LeesPlankjeDataSource : UITableViewDataSource
{
	private string[] _dataStorage;
 
	public LeesPlankjeDataSource(string[] data)
	{
		_dataStorage = data;
	}
 
	public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
	{
		UITableViewCell cell = tableView.DequeueReusableCell("plankje");
		if (cell == null)
		{
			cell = new UITableViewCell(UITableViewCellStyle.Default, "plankje");
		}
		cell.TextLabel.Text = _dataStorage[indexPath.Row];
		return cell;
	}
 
	public override int RowsInSection (UITableView tableview, int section)
	{
		return _dataStorage.Length;
	}
}

And the main.cs gets these lines:

	string[] woordjes = File.ReadAllLines("woordjes.txt");
 
	tableView.DataSource = new LeesPlankjeDataSource(woordjes);

Make sure when you have files that you want to be deployed with your app to set the build-action on the file to “content”:

BuildActionThat’s all neat. The DataSource gets it data from the outside and doesn’t care if it comes from a file or a network-connection.

So now we want some action when the user taps a row. You have no choice but to implement this in a delegate (a UITableViewDelegate of course) and then override the RowSelected() method. This method is called for you by Cocoa and as parameters you get a UITableView that the tapping happened on and the Row number that was tapped:

public class LeesPlankjeViewDelegate : UITableViewDelegate
{
	public override void RowSelected (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
	{
	}
}

But what can I do in this method? Let’s say I want to do a MessageBox-ish thing to show what word was chosen. All I have is an index to the row in my data source. But I don’t have the data source itself in this class. I need some way to acces my original data store.

I could do somehting like this:

public override void RowSelected (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
	string message = tableView.DataSource.GetCell(tableView,  indexPath).TextLabel.Text;
	using (var alert = new UIAlertView ("", message, null, "OK", null))
	{
		alert.Show ();
	}
}

So I ask the TableView for its data source, then ask the data source to give me the cell that is on the given index, and then ask the cell for the text of the label. It works, but it is butt-ugly. Why? Because now the delegate knows about the data source too. And I think it shouldn’t, because all the delegate needs to do is handle UI-interactions from the user. It should say “Hey, someone tapped me on this row, do something with it” and then leave the actual work to someone else.

I think it would be reasonable to leave the work to the controller. For me, a controller is always the man-in-the-middle, doing the real work brokering between the model and the view. But if I choose that, I have to pass in the controller when constructing the Delegate, like this:

tableView.Delegate = new LeesPlankjeViewDelegate(tableViewController);

And since the tableViewController is only known so far in the Interface Builder, I have to make the controller available in my code by creating an outlet. Sigh…. even more code in my FinishedLaunching() method. I don’t want that, I want to hook up UI-parts with each other using the Interface Builder, not in code.

So, what to do now? I don’t know yet. Let me first deal with a problem that I didn’t tell you about yet. It is in the code of the LeesPlankjeDataSource. I gave my LeesPlankjeDataSource a constuctor that accepts a string array, thereby enabling me to pass in the data that the data source needs to build a UITableView from.

The problem is, that the UISearchDisplayController re-uses my UITableViewController, including the DataSource. When I click search, it first simply overlays my view, but when I start typing in the search box, the ShouldReloadForSearchString () method gets called and that one resets the SearchResultDataSource with a filtered version of my LeesPlankjeDataSource:

public override bool ShouldReloadForSearchString (UISearchDisplayController controller, string forSearchString)
{
	Console.WriteLine("In ShouldReloadForSearchtring");
	controller.SearchResultsDataSource = new LeesPlankjeDataSource(forSearchString);
	return true;
}

And how am I gonna feed this baby with the right data? How is the SearchResultsDataSource going to get a filtered list of the words in my “woordjes.txt” file? I chose to filter by using an overloaded constructor, but I could move that code to a normal method. That allows me to use the other constructor, passing in the data as before in the main.cs:

public override bool ShouldReloadForSearchString (UISearchDisplayController controller, string forSearchString)
{
	Console.WriteLine("In ShouldReloadForSearchString");
	var woordjes = ?????????
	var data = new LeesPlankjeDataSource(woordjes);
	controller.SearchResultsDataSource = data.FilterOn(forSearchString);
	return true;
}

But where am I gonna get the “woordjes” variable from? Should I load the file again, like I did in main.cs? Or maybe my DataSource should know something about the model? In that case I will not pass data from the outside, but let the DataSource find out itself where to get the data. But that is so against the Dependency Inversion Principle (or Inversion of Control)! If classes depend on other classes, these dependencies had best be passed in from the outside, probably on construction time.

Shoot, I love the IoC pattern. Should I let it go, for the sake of simplicity? After all, I argued before that even the MVC-pattern was maybe to much of a burden for the simple applications we build on the iPhone most of the time.

For tonight, I give up. The code you can download contains the solution that goes against IoC, but works none the less.

I would love to hear your ideas about how to build iPhone applications that are tightly coherent and loosely coupled. I know that we can get in some philosophical or religious discussions, but we’ll see what to do then. I just want to learn from you guys, as much as I want to teach you where I can.

P.S.
Thanks to Alex York’s excellent post (see his comment below) I was able to improve on my code. I had seen the idea of nesting the DataSource and Delegate into the UITableViewController before, but didn’t like it then. That dislike mostly came from the fact that you have to inherit from another class. There is no tighter couling between two classes then inheritance, so I believe you should only use it when absolutely necessary. Well, by now I think that it is absolutely necessary to inherit from the classes in the Cocoa framework. It is simply the way you work.

When working on the new solution I also renamed some classes (no more Dutch names, only Dutch words in the data…) and added a class that implements the model:

public class WordsModel
{
	private List _dataStorage;
 
	public WordsModel ()
	{
		_dataStorage = File.ReadAllLines("woordjes.txt").ToList();
		_dataStorage.Sort();
	}
 
	public string[] Data {
		get { return _dataStorage.ToArray();}
	}
 
	public string[] FilterOn(string searchText)
	{
		searchText = searchText.ToLower();
		var result = _dataStorage.Where(t =&gt; t.ToLowerInvariant().StartsWith(searchText));
		return result.ToArray();
	}
}

It is the place where the knowledge of words, where they come from and how you filter them, resides.

But Alex’s solution did not solve the problem that is typical of the use of the UISearchDisplayController: you have two instances of your DataSource: the one for your initial view, that just displays all the data (words in my case), and the one that is called upon by the UISearchDelegate when you start to search and filter.

I solved this by implementing a general WordsDataSource that uses the WordsModel and has a constuctor for normal use and for filtering:

public class WordsDataSource : UITableViewDataSource
{
	private WordsModel model = new WordsModel();
	private string[] _dataStorage;
 
	public WordsDataSource()
	{
		_dataStorage = model.Data;
	}
 
	public WordsDataSource(string filter) : this()
	{
		_dataStorage = model.FilterOn(filter);
	}
 
	public string GetAt(int position)
	{
		return _dataStorage[position];
	}
 
	public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
	{
		UITableViewCell cell = tableView.DequeueReusableCell("plankje");
		if (cell == null)
		{
			cell = new UITableViewCell(UITableViewCellStyle.Default, "plankje");
		}
		cell.TextLabel.Text = _dataStorage[indexPath.Row];
		return cell;
	}
 
	public override int RowsInSection (UITableView tableview, int section)
	{
		return _dataStorage.Length;
	}
}

It inherits (yep I used the I-word) from UITableViewDataSource so can be called upon by the Cocoa framework when rendering the data for the UITableView.

And then I used that class in two places:

  1. as an internal property in my WordsTableViewController
  2. as a helper-class in the delegate of the UISearchDisplayController.
[MonoTouch.Foundation.Register("WordsTableViewController")]
public partial class WordsTableViewController : UITableViewController
{
    // Constructor invoked from the NIB loader
    public WordsTableViewController (IntPtr p) : base (p)
    {
    }
 
    // The data source for our TableView
    private WordsDataSource TableDataSource
    {
	get { return this.TableView.DataSource as WordsDataSource; }
	set { this.TableView.DataSource = value; }
    }
 
    // This class receives notifications that happen on the UITableView
    class TableDelegate : UITableViewDelegate
    {
	WordsTableViewController parentView;
        public TableDelegate (WordsTableViewController tableViewController)
        {
		parentView = tableViewController;
        }
 
        public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
        {
		string selectedWord = parentView.TableDataSource.GetAt(indexPath.Row);
		using (var alert = new UIAlertView ("Selected", selectedWord, null, "OK", null))
			alert.Show ();
        }
    }
 
    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
 
        TableView.Delegate = new TableDelegate (this);
        this.TableDataSource = new WordsDataSource ();
    }
}
[MonoTouch.Foundation.Register("WordSearchDelegate")]
public class WordSearchDelegate : UISearchDisplayDelegate
{
	public override bool ShouldReloadForSearchString (UISearchDisplayController controller, string forSearchString)
	{
		UITableViewDataSource data = new WordsDataSource(forSearchString);
		controller.SearchResultsDataSource = data;
		return true;
	}
}

I’m pretty happy with what I got by now. You can download the new version, if you like.

, , , , , , ,

9 reacties

Oredev 2009

Update 2010-05-08
The video of my session is up! Check it out at http://oredev.com/videos/dynamic-deployment-with-osgi.

This past week (4 – 6 November), I went to Øredev, probably the largest developer conference in Scandinavia. I had been invited as a speaker, thanks for having me!

Great food, nice ambiance

The first thing that strikes me about this conference is how well it has been prepared. The food is great, there is a good evening program, and overall both your inner geek and inner person are well looked after.

Highlights

Interactive Visualizations from Microsoft research – Eric Stollnitz (User Experience track)
One of the rare talks I actually did not like. Having not read the session description properly, I had totally wrong expectations; the session demoed visual tools like Photosynth, which are cool, but not something we haven’t seen before. And besides, running Vista on a Mac, and having to kill Internet Explorer…?

Open source Java: ten things you didn’t know you could do – Terrence Barr (Java track)
Early in the talk it felt like a plug for the greatness of Sun, making Java open source. Later, however, it mentions some seriously cool technology that has become possible now! Some honorable mentions,

  • The Maxine guest VM is an effort to run a Java VM directly on a HyperVisor, skipping the entire OS!
  • IKVM.NET is a project aiming to run Java code on a .NET VM. Somehow, it turns out that Java bytecode and .NET assembly language are so similar, that an effort has been started to create automatic translation tools between compiled Java and compiled .NET (and vice versa). I would like to add that this is allows not only Java to run on a .NET VM, but likely any language that runs on the Java VM! Interesting…
  • Zero writes a Java VM in plain C without using assembler code, making it easier to port it to new platforms.

The Manager’s Guide to Agile Adoption – Mike Cottmeyer (PM In Practice track)
A great talk showing the issues that might hamper agile adoption, especially in larger organizations. Some snippets,

  • “Agile adoption at the team level is not the issue, it’s adopting agile across teams.”
  • Don’t speak of features of a system, speak of capabilities (think about that one!)
  • Depending on the amount of dependencies between teams, we could use a Scrum of Scrums (resource dependency), Product Owner team (requirements dependency), or a Product Owner team with Architects (technical dependencies). Whatever method is used, this higher level should build a normalized backlog, intended to create some alignment along the teams. The team’s backlogs are based on this normalized backlog.
  • Feature teams will break down at some level; at a certain system size, it’s no longer possible to create a top-to-bottom slice of the system which is small enough for a single team to manage.

In short, I really enjoyed this talk, and I feel there might be some applications for these ideas somewhere near me…

How Exactly Can Developers Create a Compelling User Experience? – Ben Galbraith (User Experience track)
Exactly the way I would expect a user experience talk to look (and feel!) like: polished imagery, a well-oiled story line, and lots of inspiration. Besides, I have two new books to add to my reading list: About Face by Alan Cooper, which seems to be the standard volume on interaction design, and The humane interface by Jeff Raskin.

Reconsidering cherished design dogmas – Johannes Brodwall & Finn-Robert Kristensen (Architecture track)
I actually had a beer with these guys a few days earlier, and they told me about their ideas. In short, there are a number of dogmas in software design we came to hold true, but are they actually true? For instance, is generic code really more reusable than specific code?

It’s a shame the talk didn’t really come across, and I could not put my finger on the problem; it might have something to do with the over-abstracted example they chose.

Dynamic Deployment with OSGi – Angelo van der Sijpt (Java track)
Well, let the crowds decide on this one. Have you visited my talk, and have an opinion about it? Let me know in the comments?

Modeling in the Age of Agility – Kevlin Henney (Agile Architecture track)
“Working software over comprehensive documentation” sounds good, but how about modeling? Are all models potential waste? Of course not, but when applying modeling because it is modeling, is sure to create models no one will ever look at, and the few good ones in there are buried. Some snippets,

  • “Agile is all about doing.” Actually, I did not know the word ‘Agile’ comes from the Latin verb for ‘to do’.
  • “The most important aspect of modeling is the -ing.”

So?

Like I said, a great conference, and I’m sorry I missed the test track. I will leave you with a quote I picked up on Twitter (I don’t know which session it’s from),

“Don’t build frameworks, extract them”

, , , ,

Nog geen reacties

Building iPhone applications using MonoTouch, part 4: the UISearchDisplayController

In my previous post I wrote about the Interface Builder and things like outlets. Last night (with the help of some colleagues) I cracked one of the more advanced Classes in the Interface Builder: the UISearchDisplayController.

In the previous version of my application I had a UITableView and a UISearchBar. I hooked them up with some code and it worked fine. But I didn’t get the effect that you see in (for instance) the iPod application. When you scroll up,  uncover the search-bar and start typing, the original view is greyed-out. And when your search gives no result, you get a “No Results” message. Like this:

SearchScreenShot NoResults

For that, you need the UISearchDisplayController. This controller does the work of hooking up a couple of UI-elements for you:

  1. The search bar
  2. The view with the results from the search (called the searchContentsController)
  3. The delegate that handles all the events that come from the search bar and the results view (called the searchResultsDelegate)
  4. The data source that provides the data to search in (called the searchResultsDataSource)
  5. Your original view

When you drag a UISearchDisplayController and drop it at the top of your UITableViewController, all the outlets get connected to your controller automatically. Apparently the Interface Builder thinks that your class can play all these roles. This makes sense when you program Objective-C, since that language is quite capable of inheriting from lots of base classes (as is so eloquently explained in the Cocao With Love blog), but asks some more attention when used from MonoTouch.

Designers should not write code, and vice versa

I will explain again what Interface Builder does for you. Maybe you already know, but I had to get used to it, and have to keep reminding myself that you use it to, well,  build interfaces. Nothing else. Interface Builder provides a clean separation between the GUI and the code, and that’s a good thing, right? Right. I love Design Patterns, and I try to convince as much progammers as possible to take at least notice of them. But iPhone apps are mostly very small apps with one or two screens, being very good in just one or two things. Do I need to implement this whole pattern for my simple app? Well, apparently. And so will you, so let me try to help you find out how to do some of these things.

Steps to follow

Begin with a UITableViewController. Then go to the Library Window, the Objects button and drag-n-drop a UISearchDisplayController just at the top of your UITableViewController.
In the MainWindow you will have the UISearchDisplayController added. Click it there, and then go to the Outlets-tab in the Inspector Window. You will see that all the outlets will be connected to your UITableViewController, except for the searchBar-outlet since that is of course connected to the SearchBar.

When you run your app, you will have the Table-View and the SearchBar above it. When you tap the text-field you will see the desired gray-out and the “No Result” if you enter some text. So far so good.

Providing the view with data

Now we need to hook up some of our own code to the events that the UISearchDisplayController fires. Let’s start with some data in our own TableView to filter on.
As said in one of my first posts, data for a view is delivered by a DataSource. In this case a UITableViewDataSource. So add a class to your project that inherits from UITableViewDataSource. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class LeesPlankjeDataSource : UITableViewDataSource
{
	private string[] woordjes = new string[] {"aap", "noot", "mies"};
 
	public LeesPlankjeDataSource()
	{
	}
 
	public LeesPlankjeDataSource(string filter)
	{
		woordjes = woordjes.Where(f =&gt; f.StartsWith(filter)).ToArray();
	}
 
	public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
	{
		UITableViewCell cell = tableView.DequeueReusableCell("plankje");
		if (cell == null)
		{
			cell = new UITableViewCell(UITableViewCellStyle.Default, "plankje");
		}
		cell.TextLabel.Text = woordjes[indexPath.Row];
		return cell;
	}
 
	public override int RowsInSection (UITableView tableview, int section)
	{
		return woordjes.Length;
	}
}

The complete code of this solution is at the bottom of this post.

The class has two constructors. The default constructor (at line 5) is called when initializing our own view, the constructor that takes a string (at line 9) will be called when filtering is started.
The GetCell() and RowsInSection() methods need to be implemented to make your data source work. The implementation is pretty straightforward. The GetCell() method will be called “RowsInSection” times. The call to DequeueReusableCell() is some trick to limit the amount of resources that your iPhone application will use. Just make sure you pass in some string that you reuse a few lines down.

To be able to set this datasource on the table-view we have to have some programmatic access to the view. Well, we did that before in the previous post. Go to Interface Builder, select the AppDelegate in the Library Window, add an outlet and connect it to your UITableViewController. Then you can have code like this in your main.cs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public partial class AppDelegate : UIApplicationDelegate
{
	// This method is invoked when the application has loaded its UI and its ready to run
	public override bool FinishedLaunching (UIApplication app, NSDictionary options)
	{
		tableView.DataSource = new LeesPlankjeDataSource();
 
		// If you have defined a view, add it here:
		window.AddSubview (tableView);
		window.MakeKeyAndVisible ();
 
		return true;
	}
 
	// This method is required in iPhoneOS 3.0
	public override void OnActivated (UIApplication application)
	{
	}
}

We simply set the DataSource on the tableView to our own DataSource and then add the tableView to the current window. Run your app and you will have some data in your view!

Building the delegate

Now we need some code to handle the events of the search bar. The most interesting event is “ShouldReloadForSearchString()”.

Add a new class to your project that inherits from UISearchDisplayDelegate. The code should look something like this:

1
2
3
4
5
6
7
8
9
10
11
[MonoTouch.Foundation.Register("LeesPlankjeDelegate")]
public class LeesPlankjeDelegate : UISearchDisplayDelegate
{
	public override bool ShouldReloadForSearchString (UISearchDisplayController controller, string forSearchString)
	{
		Console.WriteLine("In ShouldReloadForSearchString");
		controller.SearchResultsDataSource = new LeesPlankjeDataSource(forSearchString);
		return true;
		//return base.ShouldReloadForSearchtring (controller, forSearchString);
	}
}

The first line is interesting. This is the magic that brings your classes into the Interface Builder. By registering the name of your class it will be added to the XIB (although not visible in the designer file). I’ll show you in a minute what you do with this in Interface Builder.

In the override of the ShouldReloadForSearchtring() I instantiate a new data source using the constructor that accepts a filter string. I set this on the SearchResultsDataSource property of the passed in controller object. As you can see in the code of the LeesPlankjeDataSource it will use a Lambda to filter the fixed array of words.

Hooking up the UISearchDisplayController with your Delegate

The Register-attribute on your delegate class makes it available in Interface Builder. So you go to the Library Window, choose the Objects button, then Controllers folder and then the general NSObject. Something like this: LibraryWindow

Drag it to the MainWindow, select it there, go to the Inspector, choose the Identity tab. Now change the class field to the name of your own class. LeesPlankjeDelegate in my case. Your class will not be listed, but that doesn’t matter. When you hit Enter, you’ll see in the MainWindow that both the class name and the instance name have changed. That is just fine.

Now the next magical thing: you have to connect the default delegate of the UISearchDisplayController to your Delegate class. Here is how: select the UISearchDisplayController in the MainWindow, go to the inspector, select the Outlets tab. The first outlet there is called “delegate” and is connected to your TableView. Now remove that connection by clicking the X. Then connect this delegate to your own Delegate class in the MainWindow.

Save in Interface Builder, go to MonoDevelop, run! Type something in the search and “Lo and behold!” it works!

Ain’t live sweet?

If it doesn’t work, feel free to leave a comment. I’ll see if I can help you.

Download the source code

P.S.

The last step is actually more complex than it should have been. If I make my UISearchDisplayController visible to my AppDelegate by adding an outlet, I can do with just one more line of code in my main.cs:

searchDisplayController.Delegate = new LeesPlankjeDelegate();

That way I go one-way: from Interface Builder to MonoTouch. But I thought it more interesting to go the other way too: from MonoTouch to Interface Builder.

, , , , , ,

22 reacties