Connect

  • Connect with me

  • Monday, June 21, 2010

    Decorate NBuilder to create Repeatable Sequential Generator

    Background

    In the previous post I had demonstrated the use of NBuilder to generate sequential data. This approach works fine if we need a sequence with continuous numbers. There are occasions when we need a sequence which resets or repeats itself at periodic interval.
    For eg. if we are assigning the day of week number to a list, we would want to reset the day after it reaches 7. Same way if we are writing a piece of code which counts the number of overs in an cricket match, we would want to reset the counter after 6 legitimate deliveries.
    For this demo, I would like to reuse the earlier example of person entity and a repository to access the list of persons. Lets assume I want to build a repeatable sequential list which has a minimum and maximum limit. If the order is ascending order for the generator we would like to reset the sequence to start at the minimum value once it reaches the upper bound. Also the reverse is possible i.e. if we are building a list in descending order, once we reach the lower bound, we should reset the counter to the maximum value.

    Use Decorator pattern to create Repeatable Sequential Generator

    With the above requirements in mind, let me try customizing the SequentialGenerator class provided by NBuilder. The SequentialGenerator class in NBuilder provides a Generate method which is used to generate the next number in the sequence based on the increment factor. But it does not provide a capability to reset the counter if we needed to as explained above.
    The SequentialGenerator class provides 2 virtual methods StartingWith and Advance which we can override. Advance method advances the sequence by the increment factor while the StartingWith is used to set the initial value from where the sequencing should start.
    I can use the Decorator pattern to add functionality to the sequential generator class by checking for the boundary conditions. Assuming I have a ascending sequence, I would like to restart the sequence if the max value is reached. For that reason, I can check in the Advance method if the result of Generate is going to exceed the upper bound. If that happens I can reset the sequence and start it from the minimum value using the StartingWith method. For the purpose of implementing the Decorator pattern, I’ll inherit my class named RepeatableSequentialGenerator from the SequentialGenerator class.
    public class RepeatableSequentialGenerator : SequentialGenerator<int>
        {
            private readonly SequentialGenerator<int> generator;
    
            private int maxValue = int.MaxValue;
    
            private int minValue = int.MinValue;
    
            public new int Increment
            {
                get
                {
                    return this.generator.Increment;
                }
    
                set
                {
                    this.generator.Increment = value;
                }
            }
            public new GeneratorDirection Direction
            {
                get
                {
                    return this.generator.Direction;
                }
    
                set
                {
                    this.generator.Direction = value;
    
                    this.generator.StartingWith(value == GeneratorDirection.Ascending ? this.minValue : this.maxValue);
                }
            }
    
            public RepeatableSequentialGenerator()
            {
                this.generator = new SequentialGenerator<int>();
            }
    
            public RepeatableSequentialGenerator(int minValue, int maxValue)
            {
                this.minValue = minValue;
                this.maxValue = maxValue;
                this.generator = new SequentialGenerator<int>();
            }
    
            public int MaxValue
            {
                get
                {
                    return this.maxValue;
                }
    
                set
                {
                    this.maxValue = value;
                }
            }
    
            public int MinValue
            {
                get
                {
                    return this.minValue;
                }
    
                set
                {
                    this.minValue = value;
                }
            }
    
            protected override void Advance()
            {
                int current = this.generator.Generate();
    
                if (this.Direction == GeneratorDirection.Ascending)
                {
                    if (current > this.maxValue)
                    {
                        this.generator.StartingWith(this.minValue);
                        current = this.generator.Generate();
                    }
                }
                else
                {
                    if (current < this.minValue)
                    {
                        this.generator.StartingWith(this.maxValue);
                        current = this.generator.Generate();
                    }
                }
    
                base.StartingWith(current);
            }
    
            public override void StartingWith(int nextValueToGenerate)
            {
                if (this.generator != null)
                {
                    this.generator.StartingWith(nextValueToGenerate);
                }
            }
        }


    In order to decorate the SequentialGenerator class, I need to create an instance of that within my class. If you look at the complete source code, you can also see that I am overriding the properties for Direction and Increment. These two properties act as wrappers to get or set the values on the internal sequential generator object.


    protected override void Advance()
            {
                int current = this.generator.Generate();
    
                if (this.Direction == GeneratorDirection.Ascending)
                {
                    if (current > this.maxValue)
                    {
                        this.generator.StartingWith(this.minValue);
                        current = this.generator.Generate();
                    }
                }
                else
                {
                    if (current < this.minValue)
                    {
                        this.generator.StartingWith(this.maxValue);
                        current = this.generator.Generate();
                    }
                }
    
                base.StartingWith(current);
            }


    The advance method does the trick for us. It checks what is the order of the sequence (Ascending / Descending). Based on the direction it resets the sequence if its above or below the bounds. Another point to note above is that I am doing all the calculations on the encapsulator generator class but finally calling the base.StartingWith(current). If we don’t do this, the value doesn’t get returned to the calling program.


    The below test validates the logic


    [TestMethod()]
            public void GetAllTest_ShouldReturn_SequntialPersonIds_InAscendingOrder()
            {
                const int minValue = 1;
                const int maxValue = 4;
    
                RepeatableSequentialGenerator repeatableSequentialGenerator = new RepeatableSequentialGenerator(minValue, maxValue);
                repeatableSequentialGenerator.Direction = GeneratorDirection.Ascending;
    
                const int numberOfPersons = 10;
    
                IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
                    .WhereAll().Have(x => x.Id = repeatableSequentialGenerator.Generate())
                    .Build();
    
                personRepository.Persons = persons.ToList();
    
                IList<PersonEntity> result = personRepository.GetAll();
    
                Assert.AreEqual(numberOfPersons, result.Count, "Expected and actual count is not same");
                Assert.AreEqual(result[0].Id, 1);
                Assert.AreEqual(result[1].Id, 2);
                Assert.AreEqual(result[3].Id, 4);
                Assert.AreEqual(result[7].Id, 4);
                Assert.AreEqual(result[9].Id, 2);
            }
    





    You can see that we are using the repeatableSequentialGenerator instance for generating the sequence. I have set the minimum value to 1 and maximum value to 4. So I assume that the 4th element in the persons list will have the Id as 4 and the last one will have Id as 2. I have also verified that the intermediate values are as expected.


    This is a simple case. I want to check if the increment factor works correctly or not. Below is the test which does that


    [TestMethod()]
            public void GetAllTest_ShouldReturn_SequntialPersonIds_InAscendingOrder_IncludingIncrement()
            {
                RepeatableSequentialGenerator repeatableSequentialGenerator = new RepeatableSequentialGenerator(1, 5);
                repeatableSequentialGenerator.Direction = GeneratorDirection.Ascending;
                repeatableSequentialGenerator.Increment = 2;
    
                const int numberOfPersons = 10;
    
                IList<PersonEntity> persons = Builder<PersonEntity>.CreateListOfSize(numberOfPersons)
                    .WhereAll().Have(x => x.Id = repeatableSequentialGenerator.Generate())
                    .Build();
    
                personRepository.Persons = persons.ToList();
    
                IList<PersonEntity> result = personRepository.GetAll();
    
                Assert.AreEqual(numberOfPersons, result.Count, "Expected and actual count is not same");
                Assert.AreEqual(result[0].Id, 1);
                Assert.AreEqual(result[1].Id, 3);
                Assert.AreEqual(result[2].Id, 5);
                Assert.AreEqual(result[9].Id, 1);
            }


    In this case I have set the bounds as 1 and 5 and the increment factor as 2. So I am verifying that the top 3 elements Ids are 1, 3 and 5 respectively. Also I confirm that the last element has the Id set to 1.


    Finally i also check whether the repeatable sequence works in descending mode or not.


    [TestMethod()]
            public void GetAllTest_ShouldReturn_SequntialPersonIds_InDescendingOrder()
            {
                RepeatableSequentialGenerator repeatableSequentialGenerator = new RepeatableSequentialGenerator(1, 4);
                repeatableSequentialGenerator.Direction = GeneratorDirection.Descending;
    
    
                const int numberOfPersons = 10;
    
                IList<PersonEntity> persons =
                    Builder<PersonEntity>.CreateListOfSize(numberOfPersons).WhereAll().Have(
                        x => x.Id = repeatableSequentialGenerator.Generate()).Build();
    
                personRepository.Persons = persons.ToList();
    
                IList<PersonEntity> result = personRepository.GetAll();
    
                Assert.AreEqual(numberOfPersons, result.Count, "Expected and actual count is not same");
                Assert.AreEqual(result[0].Id, 4);
                Assert.AreEqual(result[9].Id, 3);
            }

    In this test I have changes the direction of the sequence to descending. The bounds are set to 1 and 4. So in the verification section, I verify that the 1st element’s Id has the upper bound value which is 4 in this case. I also verify that the last element has the value as 3.

    Conclusion



    I wanted to keep the post short and sweet but it turned out to be a bit lengthy one. I just wanted to show how easy it is to extend NBuilder to suite our requirements. Using Decorator Pattern helped me to add functionality to the SequentialGenerator without making any changes to the existing code.


    I have uploaded the complete source code to http://dl.dropbox.com/u/1569964/RepeatableSequentialGenerator.zip. If you have any comments feel free to put them down and provide me feedback.


    Until next time Happy programming :)


    No comments:

    Post a Comment