Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I feel like I have a reusable "something" here and I'm not sure whether to think of it as a pattern or an algorithm (or neither).

It's characterized by having an unknown amount of work to accomplish a task because the subtasks can encounter various conditions which cause them to add "Issues" to a global queue.

Then, the command is run repeatedly coupled with a round of "Issue Fixing" until either there are no issues left or the number of issues does not change.

I'm just putting enough code here to show what I'm talking about - there's a bit more to it (let me know if I should post more).

        public void FindNewCampaigns()
        {
            var findNewCampaigns = Locator.Get<ICommand<IEnumerable<Campaign>>>("FindNewCampaigns");
            var campaigns = findNewCampaigns.Execute();
            var issues = Locator.Get<ICollection<Issue>>("CommandIssues");
            while (issues.Count > 0)
            {
                int before = issues.Count;
                FixIssues(issues);
                issues.Clear();
                campaigns = findNewCampaigns.Execute();
                int after = issues.Count;
                if (before == after)
                {
                    System.Console.WriteLine("No issues got fixed, quitting ({0}/{1}).", before, after);
                    break;
                }
            }
            if (issues.Count == 0)
            {
                CreateCampaigns(campaigns);
            }
        }

        private void FixIssues(ICollection<Issues.Issue> issues)
        {
            foreach (var issue in issues)
            {
                System.Console.WriteLine("ISSUE: " + issue.GetType().Name + " - " + issue.ToString());

                if (issue is AdvertiserDoesNotExist)
                {
                    var specificIssue = (AdvertiserDoesNotExist)issue;
                    var command = Locator.Get<ICommand<string, DirectAgents.SynchService.Model.EomDatabase.Advertiser>>("CreateAdvertiser");
                    command.Execute(specificIssue.AdvertiserName);
                }
                else if (issue is AccountManagerDoesNotExist)
                {
                    var specificIssue = (AccountManagerDoesNotExist)issue;
                    var command = Locator.Get<ICommand<string, DirectAgents.SynchService.Model.EomDatabase.AccountManager>>("CreateAccountManager");
                    command.Execute(specificIssue.AccountManagerName);
                }
                else if (issue is AdManagerDoesNotExist)
                {
                    var specificIssue = (AdManagerDoesNotExist)issue;
                    var command = Locator.Get<ICommand<string, DirectAgents.SynchService.Model.EomDatabase.AdManager>>("CreateAdManager");
                    command.Execute(specificIssue.AdManagerName);
                }
                else if (issue is MediaBuyerDoesNotExist)
                {
                    var specificIssue = (MediaBuyerDoesNotExist)issue;
                    var command = Locator.Get<ICommand<string, DirectAgents.SynchService.Model.EomDatabase.MediaBuyer>>("CreateMediaBuyer");
                    command.Execute(specificIssue.MediaBuyerName);
                }
            }
        }

Here's the code for FindNewCampaigns. It adds issues as it finds them. An Issue is supposed to be something that needs to happen before a new campaign is able to actually get created in a target store.

    public class FindNewCampaigns : Command<IEnumerable<Campaign>>
    {
        private IFactory<CakeEntities> cakeEntities;
        private IFactory<DADatabase> eomDatabase;

        public FindNewCampaigns(IFactory<CakeEntities> cakeEntities, IFactory<DADatabase> eomDatabase)
        {
            this.cakeEntities = cakeEntities;
            this.eomDatabase = eomDatabase;
        }

        public override IEnumerable<Campaign> Execute()
        {
            using (var eom = eomDatabase.Create())
            using (var cake = cakeEntities.Create())
            {
                // Get EOM campaigns
                var campaigns = eom.Campaigns.Select(c => c.pid).ToList();
                // Get Cake offers
                var offers = cake.CakeOffers.Select(c => c.Offer_Id).ToList();
                // Get Cake offers that don't match to EOM campaigns
                var newOffers = offers.Except(campaigns).ToList();
                // Get default values
                int accountManagerID = DefaultAccountManagerId(eom);
                int adManagerID = DefaultAdManagerId(eom);
                int advertiserID = DefaultAdvertiserID(eom);
                int campaignStatusID = DefaultCampaignStatus(eom);
                // Create new campaigns in memory
                var newCampaigns = (from offer in cake.CakeOffers
                                    where newOffers.Contains(offer.Offer_Id)
                                    select new
                                    {
                                        Offer = offer,
                                        Campaign = new Campaign {
                                            pid = offer.Offer_Id,
                                            campaign_name = offer.OfferName,
                                            campaign_status_id = campaignStatusID,
                                            account_manager_id = accountManagerID,
                                            ad_manager_id = adManagerID,
                                            advertiser_id = advertiserID,
                                        }
                                    }).ToList();
                // Set campaign status
                var campaignStatus = eom.CampaignStatus.ToDictionary(c => c.name, c => c.id);
                campaignStatus.Add("Private", campaignStatus["Active"]);
                campaignStatus.Add("Apply To Run", campaignStatus["Active"]);
                campaignStatus.Add("Inactive", campaignStatus["default"]);
                foreach (var item in newCampaigns)
                {
                    string statusName = item.Offer.StatusName;
                    if (campaignStatus.ContainsKey(statusName))
                    {
                        item.Campaign.campaign_status_id = campaignStatus[statusName];
                    }
                    else
                    {
                        AddIssue(new CampaignStatusDoesNotExist(statusName));
                    }
                }
                // Set advertiser
                var cakeAdvertisers = cake.CakeAdvertisers.ToDictionary(c => c.Advertiser_Id);
                foreach (var item in newCampaigns)
                {
                    int offerAdvertiserID = int.Parse(item.Offer.Advertiser_Id);
                    var offerAdvertiser = cake.CakeAdvertisers.FirstOrDefault(c => c.Advertiser_Id == offerAdvertiserID);
                    string offerAdvertiserName = offerAdvertiser.AdvertiserName;
                    var eomAdvertiser = eom.Advertisers.FirstOrDefault(c => c.name == offerAdvertiserName);
                    if (eomAdvertiser != null)
                    {
                        item.Campaign.advertiser_id = eomAdvertiser.id;
                    }
                    else
                    {
                        AddIssue(new AdvertiserDoesNotExist(offerAdvertiserName));
                    }
                }
                // Set account manager
                foreach (var item in newCampaigns)
                {
                    int offerAdvertiserID = int.Parse(item.Offer.Advertiser_Id);
                    var offerAdvertiser = cake.CakeAdvertisers.FirstOrDefault(c => c.Advertiser_Id == offerAdvertiserID);
                    string offerAccountManager = offerAdvertiser.AccountManagerName;
                    if (offerAccountManager != null)
                    {
                        AccountManager am = eom.AccountManagers.ToList().SingleOrDefault(c => c.NameIsEquivalentTo(offerAccountManager));
                        if (am != null)
                        {
                            item.Campaign.account_manager_id = am.id;
                        }
                        else
                        {
                            AddIssue(new AccountManagerDoesNotExist(offerAccountManager));
                        }
                    }
                    else
                    {
                        AddIssue(new OfferHasNoAccountManager(item.Offer.OfferName));
                    }
                }
                // Set ad manager
                foreach (var item in newCampaigns)
                {
                    int offerAdvertiserID = int.Parse(item.Offer.Advertiser_Id);
                    var offerAdvertiser = cake.CakeAdvertisers.FirstOrDefault(c => c.Advertiser_Id == offerAdvertiserID);
                    string offerAdManager = offerAdvertiser.AdManagerName;
                    if (offerAdManager != null)
                    {
                        AdManager ad = eom.AdManagers.ToList().SingleOrDefault(c => c.NameIsEquivalentTo(offerAdManager));
                        if (ad != null)
                        {
                            item.Campaign.ad_manager_id = ad.id;
                        }
                        else
                        {
                            AddIssue(new AdManagerDoesNotExist(offerAdManager));
                        }
                    }
                    else
                    {
                        AddIssue(new OfferHasNoAdManager(item.Offer.OfferName));
                    }
                }
                return newCampaigns.Select(c => c.Campaign).ToList();
            }
        }
share|improve this question
add comment

3 Answers

up vote 2 down vote accepted
Is this an example of a pattern or an algorithm?

It's an algorithm.

A pattern is a generalized / portable approach to solving a problem. An algorithm is a specific approach to solving a problem.

See this SO question: http://stackoverflow.com/questions/558657/whats-the-difference-between-an-algorithm-and-a-design-pattern

share|improve this answer
add comment

Not sure what answer your are looking for, but your code appears to implement a basic work item queue. I guess this would fall more under algorithm category. There's a lot of code out there that implements similar idea but I think it's too simple to declare "reusable". The overhead of having to manage this code might exceed its value of being used in multiple places.

The one problem with the code above (I guess this would fall under code review rather than programmers SE) is that it isn't very reusable/extensible as long as your FixIssues() method has a long if-else if-else if-else if-... else if-else chain. It violates open-close principle of OOD because every time you add a new issue type, you have to go back and modify the same function over and over. I'd suggest you use a factory pattern and have an external class decide which command to instantiate for a given issue type. You can use .NET's reflection to avoid having to build log if-else chains (or switch statements) and it will dynamically update whenever you add issue/command classes to your assembly, provided you assign them some unique custom attribute.

From clarity point of view, I would expect a method named "FindNewCampaigns" to do whatever work necessary to find the new campaigns and put them into some kind of collection. However, upon reading code details, that method appears to be fixing issues?? that doesn't seem to be same thing as "finding", so maybe a better name would be appropriate?

share|improve this answer
    
thanks for the feedback - seems i should have put this in code review - how does this get migrated? –  Aaron Anodide May 17 '12 at 15:12
    
re: clarity - i put the FindNewCampaigns code in. If you don't think it's a total joke (i admit i got a bit creative with this) can you tell me if your comments hold as written? Also, I intend to have the DI containter eventually locate the classes to "fix" the "issues" –  Aaron Anodide May 17 '12 at 15:16
    
flag it with belongs on codereview.stackexchange.com and it will get moved –  Jarrod Roberson May 17 '12 at 17:34
    
@AaronAnodide I asked a mod at codereview and they said it is too conceptual to be a good fit so it will not be migrated. –  maple_shaft May 17 '12 at 17:45
add comment

I think you will find such kind of algorithms when you google for keywords like event dispatching or event queue. Lots of event driven systems have such a loop at its core.

Only difference to your case is that typically in such systems new events can also come "from outside" in an asynchronous manner.

share|improve this answer
    
I figured it might be an example of something common. I'll take a look, thanks. –  Aaron Anodide May 17 '12 at 15:18
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.