Jul
13
Written by:
kchristo
Friday, July 13, 2012
One of my favorite design patterns since the age of c++ is the
Visitor Pattern. I will not explain here what the
visitor pattern is. But, if you know how to use the design pattern this is a post worth reading.
One may ask what the visitor pattern has to do with LightSwitch. Well, it doesn’t! I mean not exclusively. But the code below provides a full visitor pattern implementation background that can also be used in LS. Also, a part of the implementation is ideal for LS, since one of the challenges I had to face, when trying to widely use the visitor pattern, was make it work for “sealed” classes, classes that were not written by me and could not easily fit to my –reflection based- visitor pattern implementation. To solve this the best thing I could think of was “wrapping”. And working with LS, most of the classes (apart from the ones that belong to the domain/datasource) are actually “sealed” in the way described above.(I have to note here that this reflection-based implementation is a revision of an implementation I found (if I recall correct) in CodeProject).
First the two basic interfaces that have to be defined:
The
IVisitor interface that has to be implemented by the “worker” (or helper to follow the VB-oriented naming of the LS Team
) class.
public interface IVisitor
{
void VisitDefault(IVisitorTarget source);
}
Then the IVisitorTarget interface (implied above) that has to be implemented by the class to be “consumed/visited” by the Visitor.
public interface IVisitorTarget
{
void Accept(IVisitor visitor);
}
Ok, I know, nothing much up to now. But as I say “The longest journey starts with the first step”…(yes I am an old Chinese philosopher)
To have your visitor pattern in place you need an extension method to do the trick:
public static bool ConcreteVisit(this IVisitorTarget target, IVisitor visitor) {
Type[] types = new Type[] { target.GetType() };
MethodInfo mi = visitor.GetType().GetMethod("Visit", types);
if (mi == null)
return false;
mi.Invoke(visitor, new object[] { target });
return true;
}
Now that these pieces are in place let’s say we have a class called DisplayNameBuilder that implements the IVisitor interface. Also, let’s say we have a Customer entity in our datasource and we want it to implement the IVisitorTarget interface. All we have to do is open Customer entity in the designer, click Write Code and change the class declaration to:
public partial class Model : IVisitorTarget
make sure you are using the namespace of the class where the above extension method is implemented and implement the IVisitorTarget interface like this:
#region IVisitorTarget Members
public void Accept(IVisitor visitor) {
if (!this.ConcreteVisit(visitor))
visitor.VisitDefault(this);
}
#endregion
Also this is a sample implementation of the DisplayNameBuilder class:
public class DisplayNameBuilder : IVisitor
{
public string Name{
get;
private set;
}
public void VisitDefault(IVisitorTarget visitorTarget){
Name = visitorTarget.ToString();
}
public void Visit(Customer visitorTarget){
Name = string.Format("{0}{1}{2}", visitorTarget.FirstName, string.IsNullOrWhiteSpace(visitorTarget.FirstName) ? "", " ", visitorTarget.LastName);
}
}
In the above code please note these:
- The code was written in Live Writer as it’s demo code, so maybe it does not compile as is.
- The customer is implied to have a nullable FirstName property and a not nullable LastName (which I believe it’s a fair assumption and I agree with me).
- The implementation, as obvious, is domain aware as it knows what Customer is. This implies that the ideal place for this class to live is in the Common project.
Now lets say you create a calculated string field in Customer entity called hmmmmm…
DisplayName (surprised? It’s a gift I have regarding giving original names
). This would be the code that would implement the calculation of
DisplayName property:
partial void DisplayName_Compute(ref string result) {
DisplayNameBuilder builder = new DisplayNameBuilder();
this.Accept(builder);
result = builder.Name;
}
I HAVE to stress one more time that the code is for demonstration purposes only. It’s not just you, it IS too much fuss for nothing.
Ok, now it should be easier to understand the problem of using the visitor pattern with pre-defined classes. You cannot add the
IVisitorTarget behavior to most of the classes, automatically generated by LS.
So this is the solution to the problem.
public class IVisitorWrapper<TObjectType> : IVisitorTarget
{
public TObjectType Content {
get;
set;
}
#region IVisitorTarget Members
public void Accept(IVisitor visitor) {
if (!this.ConcreteVisit(this, visitor))
if (!this.VisitWrapper(this, visitor))
visitor.VisitDefault(this);
}
#endregion
}
This is a wrapper class (one could say it is an
IVisitorTarget decorator). If you are still with me and you keep on reading the code you have already noticed that
VisitWrapper extension method is not yet defined, so this is it:
public static bool VisitWrapper<TObjectType>(this IVisitorWrapper<TObjectType> wrapper, IVisitor visitor) {
Type[] types = new Type[] { typeof(TObjectType) };
MethodInfo mi = visitor.GetType().GetMethod("Visit", types);
if (mi == null)
return false;
mi.Invoke(visitor, new object[] { wrapper.Content });
return true;
}
Now you can wrap any type of object and use the DisplayNameBuilder to build it’s display name. Also (as –I hope- you guessed) you have to implement the respective Visit method in DisplayNameBuilder in order not go get the type name back as Name.
This is a small sample of how the wrapper can be used:
WhateverObject objectToWrap = new WhateverObject();
DisplayNameBuilder builder = new DisplayNameBuilder();
new IVisitorWrapper<WhateverObject> { Content = objectToWrap }.Accept(builder);
string displayName = builder.Name;
Well, as Porky says: That’s all Folks
. I hope that except for exhausting it was also interesting.
10 comment(s) so far...
Thank you for posting some kind of information. It was really helpful since I am doing some research now. . Dianne www.imarksweb.org
By rachelle on
Tuesday, June 24, 2014
|
i enjoyed reading your blog post. thanks for sharing this informative one. till next time.
www.triciajoy.com
By dhanne on
Tuesday, July 22, 2014
|
Love it! Very interesting topics, I hope the incoming comments and suggestion are equally positive. Thank you for sharing this information that is actually helpful.
ufgop.org ufgop.org
By ufgop.org on
Wednesday, November 26, 2014
|
Looking for a good site that is more interesting and knowledgeable? Here are some information that may useful for future reference. Visit our page @ www.gofastek.com..
cheun
By gofastek.com on
Wednesday, December 17, 2014
|
understand the problem of using the visitor pattern with pre-defined classes. visit my site too, check here chevysilverado2015.com
By michele on
Friday, September 11, 2015
|
Nice Info, Thank you for sharing this information that is actually helpfuly.
Visit 2016bestcarstrend.com
By nailuns on
Monday, September 14, 2015
|
Keep posting the articles,useful to every one. thank you
Don't forget visit bestluxurycars2016.com
By azkiya on
Monday, September 14, 2015
|
Interesting topic. I would like to add a little bit different topics
Please visit 2016carsreleaseprice.com
2016carsreleaseprice.com
Thanks You
By aboa on
Monday, September 14, 2015
|
I really enjoyed reading your article. I found this as an informative and interesting post, so i think it is very useful and knowledgeable. I would like to thank you for the effort you have made in writing this article.
edupdf.org
By leewoo880 on
Thursday, September 29, 2016
|
Nike Sneakers For Men Jordan Shoes Nike Mags Nike Shoes Pandora Earrings Air Force 1 Christian Louboutin Shoes Pandora Necklace For Women Nike Shoes 2019 Nike Outlet Store Yeezy 500 Black Jordan 11 Gamma Blue Nike Running Shoes For Men Pandora Necklace Louboutin shoes Pandora Jewelry Outlet Pandora Earrings Nike Outlet Store Louboutin Shoes Pandora Louboutin Shoes Nike Free Run Nike Air Zoom Pegasus 36 Christian Louboutin Shoes Pandora Canada Ferragamo Shoes Jordan Retro Nike Outlet Adidas Stan Smith Nike Outlet Online Lebron 16 Ferragamo Christian Louboutin Nike Outlet Online Nike Air Force Ones NMD R1 Nike Huarache Nike Shoes Air Jordan Sneakers Ultra Boost Adidas Nike Shoes Pandora Bracelets For Women Christian Louboutin Outlet Nike Factory Store Online Nike Shoes For Kids Nike Running Shoes Yeezy Nike Outlet Air Jordans Pandora Charms Air Max Pandora Official Website Lebron James Sneakers Nike Sneakers Sale Red Bottom Shoes Ultra Boost Pandora Canada Moncler UK Nike Outlet Store Online Pandora Rings Nike Lebron 16 Air Force 1 Mid Yeezy Sneakers Nike Epic React Basketball Shoes Air Max 2019 Nike Outlet Store Nike Running Shoes For Men Yeezys Boost 350 V2 Michael Jordan Shoes Nike Shoes Kevin Durant Shoes Nike Zoom Pegasus NMD Christian Louboutin Shoes Outlet Air Max 95 Pandora Canada Lebron 17 Low Jordan Shoes For Kids Kyrie Basketball Shoes Nike Shoes Red Bottom Shoes Christian Louboutin Shoes Nike Air Max 270 Air Max 98 Nike Cortez Women Christian Louboutin Outlet Nike Outlet Store Air Max 98 Nike Clearance Outlet Pandora Pandora Charms Lebron James Shoes Pandora Charms Adidas Sneakers For Men Nike Shoes Cheap Nike Yeezy Boost 750 Red Bottom Heels Nike Shoes Nike M2k Tekno Nike Sneakers Vans Nike Presto Fjallraven Backpack Nike Shoes 2019 Nike Nike Black Friday Sale Jordan Retro Ferragamo Louboutin Outlet Kyrie Irving Basketball Shoes Valentino Nike Air Max 720 Nike Air Max 720 Cheap Yeezys Mens Nike Shoes Nike Factory Outlet Adidas Yeezys Sneakers Website Nike Metcon Basketball Shoes Nike Christian Louboutin Shoes Yeezy Nike Outlet Store Jordan 11 Yeezy Shoes Fjallraven Backpack Nike Air Max 90 Nike Outlet Nike Clearance Store Nike Store Online Golden Goose Adidas Yeezys Louboutin Outlet Pandora Bracelets Asics Shoes Nike Air Force Jordan 13 Retro Nike Outlet Nike Outlet Store Online Shopping Nike Clearance
By Anthony on
Thursday, January 6, 2022
|