ISubmitBlogPosts - a nice twist on Apps Hungarian Notation for Interfaces...
I've been pairing with Andy Palmer over this last week. I have to say it's been a lot of fun... and I think we've learned a lot from the experience. One of the things I learned from Andy this week was an innovative use of Hungarian notation for interfaces... Andy told me about Udi Dahan's presentation on intentions and interfaces (PDF)
Basically, Udi suggests an innovative usage of Apps Hungarian Notation...
Hungarian Notation is when you give a prefix that indicates the type of something... for example, strName to indicate it is a string... this is rarely used now. Apps Hungarian Notation, however, is used in C# where interfaces are prefixed with the letter "I". For example, "ISerializable" instead of just "Serializable".
Many argue against this... however... Udi suggests an innovative twist... Using the "I" prefix. So, instead of ISerializable you would call the interface ISerializeObjects. I really, really like this approach!!
IWasPairingWithAndy
Andy and I were writing a Web Browser Facade in Java that abstracts Actions performed by automated acceptance tests from Selenium RC. We'd started to use Udi's approach... for example, we had a IPerformAnAction interface implemented by classes like LoginAction and an IHaveAnXPath interface for an enum that returns the XPath locator for an element... for example LoginPage.USER_NAME_FIELD.getXPath().
IHaveAProblemToSolve
These tests need to work with AJAX applications so waiting for pages to load doesn't work... You have to wait for a condition to be true... Under the hood of this Web Browser Facade is Selenium... In Selenium there is a waitForCondition() method. We didn't want to expose this and wanted to make it easy to add new conditions... specifically, we didn't want to have to write javascript for every condition...
Unfortunately, we got to discuss the ideas but didn't get to finish it on Friday... so I had a bash at sketching out the idea on Saturday morning... So, what we want to be able to do in our Actions (that implement IPerformAnAction) is write:
browser.waitUntil(CustomerSearch.LAST_NAME_FIELD, isPresent());
INeedSomeCode
So, we need a new method in our browser...
public interface IBrowseTheWeb
{
//browser's existing methods...
void waitUntil (IHaveAnXpath element, ICheckAnElement condition);
}
And we already have:
public interface IEvaluateACondition
{
boolean isSatisfied(IHaveAnXPath element);
}
public interface IHaveAnXPath
{
String getXPath();
}
IHaveAnXPath
So, looking at our IHaveAnXPath.... let's say the application has a 'Last Name' field on the customer search view... We would have an enum that implements IHaveAnXPath for CustomerSearch that has LAST_NAME_FIELD("//some/xpath"); that returns this xpath when you call CustomerSearch.LAST_NAME_FIELD.getXPath();
ICheckAnElement
So, for our conditions we now need something like:
public interface ICheckAnElement
{
ICheckAnElement checking(IHaveAnXPath element);
IEvaluateACondition usingThis(IBrowseTheWeb browser);
}
This is so that the browser's waitUntil(IHaveAnXpath element, ICheckAnElement condition) method can call the condition as follows...
IBrowseTheWeb browser=this;
if(condition.checking(element).usingThis(browser).isSatisfied()) {
return;
}
That is of course wrapped in a loop.
Oh, let's not forget the method isPresent(), well that's a static method on our implementation of the condition (used as a static import earlier)...
IWantToKnowHowToImplementThis
So, how does this work? Well, we need an ElementIsPresentCondition that has a static method isPresent() that is a factory method returning an ICheckAnElement.
We also need an instance method checking() that adds data to the object, returning ICheckAnElement...
And we need to tell our condition that it is usingThis(browser), returning an IEvaluateACondition so that we can see if the condition is satisfied...
public class ElementIsPresentCondition implements IEvaluateACondition, ICheckAnElement
{
private IHaveAnXPath elementToCheck;
private IBrowseTheWeb browser;
private ElementIsPresentCondition()
{//cannot be constructed externally
}
public static ICheckAnElement isPresent()
{
return new ElementIsPresentCondition ();
}
@Override
public ICheckAnElement checking(IHaveAnXPath elementLocator)
{
elementToCheck = elementLocator;
return this;
}
@Override
public IEvaluateACondition usingThis(IBrowserTheWeb browser)
{
assert(elementToCheck!=null);
this.browser=browser;
return this;
}
@Override
public boolean isSatisfied()
{
return browser.getXPathCountFor(elementToCheck) > 0;
}
}
This can be applied to checking to see if an element has some other property... such as text...
browser.waitUntil(Application.ACTIVE_USER,contains("Antony Marcano"));
The method contains() is a static method on a ElementContainsTextCondition...
public class ElementContainsTextCondition implements IEvaluateACondition,ICheckAnElement
{
private String expectedText;
private IHaveAnXPath elementToCheck;
private IBrowseTheWeb browser;
private ElementContainsTextCondition (String expectedText)
{
this.expectedText=expectedText;
}
public static ICheckAnElement contains(String expectedText)
{
return new ElementContainsTextCondition(expectedText);
}
@Override
public ICheckAnElement checking(IHaveAnXPath element)
{
elementToCheck = element;
return this;
}
@Override
public IEvaluateACondition usingThis(IBrowseTheWeb browser)
{
assert(elementToCheck!=null);
this.browser=browser;
return this;
}
@Override
public boolean isSatisfied()
{
if (browser.getXPathCountFor(elementToCheck)>0)
{
String actualText = browser.getTextFor(elementToCheck);
return actualText.equals(expectedText);
}
throw new ElementNotPresentException("Failed to locate :" + nameOf(elementToCheck));
}
}
This still works the same from within waitUntil():
if(condition.checking(element).usingThis(browser).isSatisfied()) {
return;
}
ILikeThisStyleOfNamingInterfaces
I love the way this all reads... Of course some inspiration for some aspects of the general coding style I've used has been inspired by Hamcrest (although I'm not using Hamcrest itself) but I really like how Udi's interface naming approach enriches the readability of the code...
Ok, so the section headings in this blog post might be a bit much (but I thought that would make the post a little more fun)... but I've found it makes my code so much more expressive... I love that... So my thanks go to Andy for telling me about Udi's approach!!
I mean... c'mon... check out this declaration!!
public static ICheckAnElement contains(String expectedText){...}
I love it!
Anyways.... all this is just sketching it out in an experimental sort of way... a spike if you will... let's see how it turns out when Andy and I have to implement for real... and then hand this over to our mutual client...
Oh and one last thing...
IWantToRemoveDuplication
Spotted the duplication?? Sure... checking() and usingThis(browser) could be pulled up into a AbstractBrowserCondition but I'm not doing that now... time to go enjoy the sunny weather on a Sunday afternoon :-)
