What you want to see on DotNetFunda.com ?
Go to DotNetFunda.com
Twitter TwitterLinkedIn
YouTubeGoogle
 Online : 54027 |  Welcome, Guest!   Register  Login
Home > Articles > Pattern and Practices > Let us learn Template Design Pattern

Let us learn Template Design Pattern

3 vote(s)
Rating: 5 out of 5
Article posted by Niladri.Biswas on 6/17/2012 | Views: 3396 | Category: Pattern and Practices | Level: Beginner | Points: 250 red flag


In this article we will learn the Template Design pattern with a real time example

Download


 Download source code for Let us learn Template Design Pattern


Introduction

Let us first start with a problem/case study because of which I will write this article

Problem statement

There are two sources of data and from those sources, we need to

  1. form two different email body (Template pattern will act here)
  2. compose them into a single one
  3. and then need to send the mail

Well, the statement is not so clear..right?Let us see what we are trying to discuss

First Entity:Exception Report Entity

public class ExceptionReport

{

public int SalesOrderId { get; set; }

public string ResellerPartnerId { get; set; }

public string ResellerName { get; set; }

public string DistributorPartnerId { get; set; }

public string DistributorName { get; set; }

public string ExceptionDescription { get; set; }

}

This is an Exception Report entity where some abnormal/unexpected reports will be stored

Second Entity:PartnerNotRegistered Entity

public class PartnerNotRegistered

{

public int OrderNo { get; set; }

public string PartnerId { get; set; }

public string PartnerType { get; set; }

public string CountryName { get; set; }

}

This is a Partner Not Registered entity where records pertaining to those partners will be stored who are not registereed to the system.

Now, two different email bodies needs to be prepared from the sources

The email content of the first entity (ExceptionReport) will be

The email content of the second entity (PartnerNotRegistered) will be

Then these two needs to be merged and the structure after merging will be

Finally these needs to be emailed to the respective participants

Existing code - The traditional linear way/approach

public class ExistingTradionalWay

{

public void ComposeMergeAndSendMail()

{

try

{

#region Email Composition For Exception Reports

StringBuilder sbEmailMsg1 = new StringBuilder();

//source data

var exceptionSourceRecord = Source.GetExceptionReports();

//Email header

sbEmailMsg1.Append("Exception Report");

sbEmailMsg1.Append("<table width='100%' border='1'>");

sbEmailMsg1.Append("<tr>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append("<b>SalesOrderId</b>");

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append("<b>ResellerPartnerId</b>");

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append("<b>ResellerName</b>");

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append("<b>DistributorPartnerId</b>");

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append("<b>DistributorName<b/>");

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append("<b>ExceptionDescription</b>");

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("</tr>");

//Email body

foreach (ExceptionReport excRecord in exceptionSourceRecord)

{

sbEmailMsg1.Append("<tr>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append(excRecord.SalesOrderId);

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append(excRecord.ResellerPartnerId);

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append(excRecord.ResellerName);

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append(excRecord.DistributorPartnerId);

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append(excRecord.DistributorName);

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("<td>");

sbEmailMsg1.Append(excRecord.ExceptionDescription);

sbEmailMsg1.Append("</td>");

sbEmailMsg1.Append("</tr>");

}

//Email tail

sbEmailMsg1.Append("</table>");

var EmailMsg1 = sbEmailMsg1.ToString();

#endregion

#region Email Composition For Partner Not Registered

StringBuilder sbEmailMsg2 = new StringBuilder();

//source data

var partnerNotRegisteredSourceRecord = Source.GetPartnerNotRegistered();

//Email header

sbEmailMsg2.Append("Partner Not Registered");

sbEmailMsg2.Append("<table width='100%' border='1'>");

sbEmailMsg2.Append("<tr>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append("<b>OrderNo</b>");

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append("<b>PartnerId</b>");

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append("<b>PartnerType</b>");

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append("<b>CountryName</b>");

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("</tr>");

//Email body

foreach (PartnerNotRegistered pnr in partnerNotRegisteredSourceRecord)

{

sbEmailMsg2.Append("<tr>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append(pnr.OrderNo);

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append(pnr.PartnerId);

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append(pnr.PartnerType);

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("<td>");

sbEmailMsg2.Append(pnr.CountryName);

sbEmailMsg2.Append("</td>");

sbEmailMsg2.Append("</tr>");

}

//Email tail

sbEmailMsg2.Append("</table>");

var EmailMsg2 = sbEmailMsg2.ToString();

#endregion

#region Compose both the email contents

var emailList = new string[] { EmailMsg1, EmailMsg2 };

StringBuilder sbEmailContent = new StringBuilder();

for (int i = 0; i < emailList.Length; i++)

{

sbEmailContent.AppendLine(emailList[i]);

}

var composedEmailContent = sbEmailContent.ToString();

#endregion

#region Send the mail

SendMail(composedEmailContent);

#endregion

}

catch (Exception ex)

{

throw ex;

}

}

private void SendMail(string composedEmailContent)

{

try

{

SmtpClient mailClient = new SmtpClient();

MailMessage mailMessage = new MailMessage("from@from.com", "to@to.com");

mailClient.Host = "localhost";

mailMessage.Subject = "Composed Email";

mailMessage.Body = composedEmailContent;

mailMessage.IsBodyHtml = true;

mailClient.Send(mailMessage);

}

catch (Exception ex)

{

throw ex;

}

}

}

This program works fine but look into the current implementation.There are some drawbacks

  1. Difficult to maintain
  2. Difficult to extend the existing functionality
  3. A single class has been asked to perform multiple tasks(Prepare individual mail content, Compose them, Send them)
  4. Not at all suitable for testing
  5. In future if more entities participate, it will be difficult to code, test etc.
  6. Duplication of code/pattern
  7. Does not satisfy SOLID OO Design principle

Rescuer

Template design pattern.

What is Template design pattern?

It provides an implementation in a derived class that needs to be used by the base class.It comes under the category of Behavioral Patterns.

Pattern Components

The template pattern comprises the following components:

  1. AbstractClass:It defines an abstract and a non-abstract method
  2. ConcreteClass: It overrides the behaviour of the abstract method

UML Class diagram

Can we see the step by step implementation?

Definitely.First of all let us create an abstract class by the name "EmailTemplate.cs"

abstract class EmailTemplate

{

public abstract string GetEmailContent();

public object SourceType { get; set; }

public string GetEmailTemplate()

{

return GetEmailContent();

}

}

This class has an abstract method GetEmailContent() that will be implemented by the concrete classes. There is a non-abstract method, that will be impleneted in the client class.Also the client class has to register the data souce for that to pass to the respective concreate classes.Henceforth, we have the SourceType property.

Then create the first concreate class "ExceptionReportEmailTemplate.cs" that implements the "EmailTemplate.cs" abstract class and override it's GetEmailContent() method

class ExceptionReportEmailTemplate : EmailTemplate

{

StringBuilder sbEmailMsg;

List<ExceptionReport> lstExceptionReport;

public override string GetEmailContent()

{

sbEmailMsg = new StringBuilder();

lstExceptionReport = SourceType as List<ExceptionReport>;

ComposeEmail();

return sbEmailMsg.ToString();

}

#region Private methods

private void ComposeEmail()

{

EmailHeader();

EmailBody();

EmailFooter();

}

private void EmailHeader()

{

sbEmailMsg.Append("Exception Report");

sbEmailMsg.Append("<table width='100%' border='1'>");

sbEmailMsg.Append("<tr>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("<b>SalesOrderId</b>");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("<b>ResellerPartnerId</b>");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("<b>ResellerName</b>");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("<b>DistributorPartnerId</b>");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("<b>DistributorName<b/>");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("<b>ExceptionDescription</b>");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("</tr>");

}

private void EmailBody()

{

foreach (ExceptionReport excRecord in lstExceptionReport)

{

sbEmailMsg.Append("<tr>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(excRecord.SalesOrderId);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(excRecord.ResellerPartnerId);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(excRecord.ResellerName);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(excRecord.DistributorPartnerId);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(excRecord.DistributorName);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(excRecord.ExceptionDescription);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("</tr>");

}

}

private void EmailFooter()

{

sbEmailMsg.Append("</table>");

}

#endregion

public List<ExceptionReport> SourceType { get; set; }

}

A similar kind of implementation has been done for "PartnerNotRegisterEmailTemplate.cs" class

class PartnerNotRegisterEmailTemplate : EmailTemplate

{

StringBuilder sbEmailMsg;

List<PartnerNotRegistered> lstPartnerNotRegistered;

public override string GetEmailContent()

{

sbEmailMsg = new StringBuilder();

lstPartnerNotRegistered = SourceType as List<PartnerNotRegistered>;

ComposeEmail();

return sbEmailMsg.ToString();

}

#region Private methods

private void ComposeEmail()

{

EmailHeader();

EmailBody();

EmailFooter();

}

private void EmailHeader()

{

sbEmailMsg.Append("Partner Not Registered");

sbEmailMsg.Append("<table width='100%' border='1'>");

sbEmailMsg.Append("<tr>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("OrderNo");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("PartnerId");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("PartnerType");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append("CountryName");

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("</tr>");

}

private void EmailBody()

{

foreach (PartnerNotRegistered pnr in lstPartnerNotRegistered)

{

sbEmailMsg.Append("<tr>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(pnr.OrderNo);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(pnr.PartnerId);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(pnr.PartnerType);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("<td>");

sbEmailMsg.Append(pnr.CountryName);

sbEmailMsg.Append("</td>");

sbEmailMsg.Append("</tr>");

}

}

private void EmailFooter()

{

sbEmailMsg.Append("</table>");

}

#endregion

}

At this stage we can figure out that if some more concrete requirement comes in future then we can easily handle that by creating another concrete class and they are entirely loosely coupled, concentrate on their own way of implementation and is responsible for their own unit.It helps to easily test also the individual modules

Now let us have a client class (say ClientApplication.cs) that will interact with the concrete classes, will compose the emails and later send

class ClientApplication

{

public void ComposeMergeAndSendMail()

{

var exceptionReportEamilTemplate = GetEmailcontentForExceptionReport();

var partnerNotRegisterEmailTemplate = GetEmailcontentForPartnerNotRegister();

//Compose mails

var composedEmails = ComposeEmail.ComposeEmails(new string[] { exceptionReportEamilTemplate, partnerNotRegisterEmailTemplate });

//Send the email

EmailSender.SendMail(composedEmails);

}

private string GetEmailcontentForExceptionReport()

{

//source data

List<ExceptionReport> lstExceptionReport = Source.GetExceptionReports();

string exceptionReportEamilTemplate = string.Empty;

if (lstExceptionReport.Count > 0)

{

var template = new ExceptionReportEmailTemplate();

template.SourceType = lstExceptionReport; //assign the Source type

exceptionReportEamilTemplate = template.GetEmailTemplate(); //Get request specific email template.

}

return exceptionReportEamilTemplate;

}

private string GetEmailcontentForPartnerNotRegister()

{

//source data

List<PartnerNotRegistered> lstPartnerNotRegistered = Source.GetPartnerNotRegistered();

string partnerNotRegisterEmailTemplate = string.Empty;

if (lstPartnerNotRegistered.Count > 0)

{

var template = new PartnerNotRegisterEmailTemplate();

template.SourceType = lstPartnerNotRegistered;//assign the Source type

partnerNotRegisterEmailTemplate = template.GetEmailTemplate(); //Get request specific email template.

}

return partnerNotRegisterEmailTemplate;

}

}

As we can make out that the code is very much cleaner.Here every class is performing only one responsibility. The "ComposeEmail.cs" file is meant for only composition of emails and "EmailSender.cs" is responsible for for Sending emails

The code for "ComposeEmail.cs" is as under

public class ComposeEmail

{

public static string ComposeEmails(params string[] emailList)

{

StringBuilder sbEmailContent = new StringBuilder();

var cnt = emailList.Length;

for (int i = 0; i < cnt; i++)

{

sbEmailContent.AppendLine(emailList[i]);

}

return sbEmailContent.ToString();

}

}

The code for "EmailSender.cs" is as under

public static void SendMail(string mailContent)

{

try

{

SmtpClient mailClient = new SmtpClient();

MailMessage mailMessage = new MailMessage("from@from.com", "to@to.com");

mailClient.Host = "localhost";

mailMessage.Subject = "Composed Email";

mailMessage.Body = mailContent;

mailMessage.IsBodyHtml = true;

mailClient.Send(mailMessage);

}

catch (Exception ex)

{

throw ex;

}

}

N.B.~One more note to pass that is the "ComposeEmail.cs" and "EmailSender.cs" are not part of the Template Design Pattern.It has been included to show the entire flow so that new comers can appretiate it's usage in real time scenario

Advantages of the scenario after implementation of the Template Pattern

  1. Every class is responsible of their of unit of activity
  2. Easily maintainable
  3. Easy to test
  4. Compeltely loosely coupled
  5. Can be easily extended without dependency of any of the existing code
  6. Follows SOLID design principle

References

  1. Template method pattern
  2. Template Method

Conclusion

Hope this article has given a good insight as when to use template pattern , how to design using SOLID principle etc.Comments are highly appreciated.Zip file is attached

Disclaimer~This is a real time problem I recently encounter.Here I have proposed my approach of solving the problem.There can be other ways also of doing so.Users are free to apply their own thoughts.However, this article is meant basically for teaching the purpose of implementation of template pattern.

If you like this article, subscribe to our RSS Feed. You can also subscribe via email to our Interview Questions, Codes and Forums section.

Page copy protected against web site content infringement by Copyscape
Found interesting? Add this to:



Please Sign In to vote for this post.

Experience:6 year(s)
Home page:http://www.dotnetfunda.com
Member since:Monday, October 25, 2010
Level:Diamond
Status: [Member]
Biography:Lead Engineer at HCL Technologies Ltd., having 6 years of experience in IT field.
I love to explore new technologies and love challenges and try to help others as much as possible not only by coding but also by all possible means.
 Responses
Posted by: Srilu.Nayini577 | Posted on: 17 Aug 2012 09:25:35 AM | Points: 25

Nice article.Please post more article which is most useful to freshers(<1exp)

>> Write Response - Respond to this post and get points
Related Posts

This section will cover Collaboration diagrams, Activity diagrams, Chart Diagrams, Package diagrams, Component diagrams, Deployment diagrams.

Design Patterns or one of the key things in Software Development. These patterns will help to improve coding style and also improves the productivity during development or while design

This Section will cover use case diagrams, primary and secondary actors, primary and secondary actors, Extend’ and ‘Include, class diagrams, protected in class diagrams, class diagram mean, composition in class diagrams, association in class diagrams, service class, entity and service class, generalization and specialization, abstract class and interface , object diagrams , sequence diagrams.

Interpeter , Iterator , Mediator , Memento and Observer design patterns.

When we consume business objects in UI, the logic can become pretty complex for activities like setting the business object value, getting the object value and setting the user interface from the object value. We can minimize the complexity of UI interaction with the business object using mediator pattern.

More ...
About Us | Contact Us | The Team | Advertise | Software Development | Write for us | Testimonials | Privacy Policy | Terms of Use | Link Exchange | Members | Go Top
General Notice: If you find plagiarised (copied) contents on this page, please let us know the original source along with your correct email id (to communicate) for further action.
Copyright © DotNetFunda.Com. All Rights Reserved. Copying or mimicking the site design and layout is prohibited. Logos, company names used here if any are only for reference purposes and they may be respective owner's right or trademarks. | 5/24/2013 9:42:17 AM