Pregunta

An employee’s salary is calculated from two elements – Basic and StandardPoint. One employee will get one Basic and one or more (or no) StandardPoint each month.

There are various StandardPoints namely – StarPerformerPoint , RecognitionPoint , ReferralPoint, BrandingPoint.

There is a functionality to calculate TotalAnnualSalary. In a year, an employee is eligible to avail a maximum of 4 standards points. Even if he gets more points only 4 will be calculated for total annual salary calculation.

I have following code. It works fine. However there is a non-efficient memory utilization. The StandardPointElement is created multiple times.

How can we optimize the object creation in this scenario?

UPDATE

Can we use something like flyweight pattern?

Wikipedia says about Flyweight pattern

A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.

In the article Flyweight Design Pattern - C#, Shailendra Chauhan mentions the following

Extrinsic data is computed on the fly means at runtime and it is held outside of a flyweight object. Hence it can be stateful.

Why is the Flyweight Pattern practical?

Code

interface ISalaryScoreElement
{
    int SalaryScore { get; }
}

public class BasicElement : ISalaryScoreElement
{
    public int SalaryScore
    {
        get
        {
            return 100;
        }
    }

}

public class StandardPointElement : ISalaryScoreElement
{
    public int SalaryScore
    {
        get
        {
            return 10;
        }
    }

}

Employee Class

class Employee
{
    private List<string> eligibility;
    public List<string> EligibleSalaryTypes
    {
        get
        {
            return eligibility;
        }
    }

    public Employee(List<string> eligibility)
    {
        this.eligibility = eligibility;
    }

    public int GetTotalAnnualSalary()
    {
        int totalSalary = 0;
        ISalaryScoreElement sal = null;
        CalculatorFactory factory = new CalculatorFactory();

        int occurenceCountForStandardPoint = 0;

        foreach (string salaryType in EligibleSalaryTypes)
        {
            switch (salaryType)
            {
                case "Basic":
                    sal = factory.GetSalaryElement("BasicElement");
                    break;

                case "ReferralPoint":
                    sal = factory.GetSalaryElement("StandardPointElement");
                    break;

                case "BrandingPoint":
                    sal = factory.GetSalaryElement("StandardPointElement");
                    break;

                case "RecognitionPoint":
                    sal = factory.GetSalaryElement("StandardPointElement");
                    break;

                case "StarPerformerPoint":
                    sal = factory.GetSalaryElement("StandardPointElement");
                    break;

                default:
                    throw new Exception("No mapping available");
            }

            if (sal is StandardPointElement)
            {
                occurenceCountForStandardPoint++;
                if (occurenceCountForStandardPoint > 4)
                {
                    //StandardPointElement can be considered a maximum of 4 times for salary calculation
                    continue;
                }
            }


            totalSalary = totalSalary + sal.SalaryScore;

        }

        return totalSalary;
    }



}

Factory

class CalculatorFactory
{
    public ISalaryScoreElement GetSalaryElement(string salaryKey)
    {
        ISalaryScoreElement c = null;
        switch (salaryKey)
        {
            case "BasicElement":
                c = new BasicElement();
                break;
            case "StandardPointElement":
                c = new StandardPointElement();
                break;
            default:
                throw new Exception("Factory cannot create the object specified");
        }
        return c;
    }
}

Client

class Program
{
    static void Main(string[] args)
    {
        List<string> eligibleBonus = new List<string>();

        //For January 2013
        eligibleBonus.Add("Basic");
        eligibleBonus.Add("StarPerformerPoint");

        //For February 2013
        eligibleBonus.Add("Basic");
        eligibleBonus.Add("StarPerformerPoint");
        eligibleBonus.Add("ReferralPoint");

        //For March 2013
        eligibleBonus.Add("Basic");
        eligibleBonus.Add("BrandingPoint");
        eligibleBonus.Add("RecognitionPoint");

        //For April 2013
        eligibleBonus.Add("Basic");
        eligibleBonus.Add("BrandingPoint");


        Employee e = new Employee(eligibleBonus);
        int effectiveSalary =  e.GetTotalAnnualSalary();


        Console.WriteLine(effectiveSalary);
        Console.ReadKey();
    }

}
¿Fue útil?

Solución 4

I am using flyweight pattern. [But this may not be the best solution]

enter image description here

REFERENCES

  1. Implementing the Singleton Pattern in C# - Jon Skeet talks about Thread Safety also.
  2. Why is the Flyweight Pattern practical?
  3. Flyweight Design Pattern - C#

Flyweight Factory

class CalculatorFlyweightFactory
{
    Dictionary<string, ISalaryScoreElement> calculators = new Dictionary<string, ISalaryScoreElement>();

    public int TotalObjectsCreated
    {
        get { return calculators.Count; }
    }

    public ISalaryScoreElement GetSalaryElement(string salaryKey)
    {
        ISalaryScoreElement c = null;
        if (calculators.ContainsKey(salaryKey))
        {
            c = calculators[salaryKey];
        }
        else
        {
            switch (salaryKey)
            {
                case "BasicElement":
                    c = new BasicElement();
                    calculators.Add("BasicElement", c);
                    break;
                case "StandardPointElement":
                    c = new StandardPointElement();
                    calculators.Add("StandardPointElement", c);
                    break;
                default:
                    throw new Exception("Factory cannot create the object specified");
            }
        }
        return c;
    }
}

Flyweights

interface ISalaryScoreElement
{
    int SalaryScore { get; }

    //Newly Added
    int OccurenceCount { get; set; }
}

public class BasicElement : ISalaryScoreElement
{
    public int SalaryScore
    {
        get
        {
            return 100;
        }
    }

    public int OccurenceCount { get; set; }
}

public class StandardPointElement : ISalaryScoreElement
{
    public int SalaryScore
    {
        get
        {
            return 10;
        }
    }

    public int OccurenceCount { get; set; }
}

Function

    public int GetTotalAnnualSalary()
    {
        int totalSalary = 0;

        ISalaryScoreElement sal = null;
        CalculatorFlyweightFactory flyweightFactory = new CalculatorFlyweightFactory();

        foreach (string salaryType in EligibleSalaryTypes)
        {
            switch (salaryType)
            {
                case "Basic":
                    sal = flyweightFactory.GetSalaryElement("BasicElement");
                    break;

                case "ReferralPoint":
                    sal = flyweightFactory.GetSalaryElement("StandardPointElement");
                    break;

                case "BrandingPoint":
                    sal = flyweightFactory.GetSalaryElement("StandardPointElement");
                    break;

                case "RecognitionPoint":
                    sal = flyweightFactory.GetSalaryElement("StandardPointElement");
                    break;

                case "StarPerformerPoint":
                    sal = flyweightFactory.GetSalaryElement("StandardPointElement");
                    break;

                default:
                    throw new Exception("No mapping available");
            }

            if (sal is StandardPointElement && sal.OccurenceCount >= 4)
            {
                //StandardPointElement can be considered a maximum of 2 times for salary calculation
                continue;
            }

            sal.OccurenceCount = sal.OccurenceCount + 1;
            totalSalary = totalSalary + sal.SalaryScore;

        }

        return totalSalary;
    }

Otros consejos

It sounds like you want to use the singleton pattern for StandardPointElement. To do this create a static field to hold the single value. Consumers will use this value instead of stating new StandardElementPoint(). Also I made the constructor protected to prevent consumers from accidentally creating new values

public class StandardPointElement : ISalaryScoreElement
{
    public static readonly StandardPointElement Instance = 
       new StandardPointElement();

    protected StandardPointElement() { } 

    public int SalaryScore
    {
        get
        {
            return 10;
        }
    }
}

New convert all uses of new StandardElementPoint in your code to StandardElementPoint.Instance

Call both factory.GetSalaryElement("BasicElement"); and factory.GetSalaryElement("StandardPointElement"); only once, before the loop starts, and reuse the objects in the loop.

Or, if you really have nothing better to do, have one internal dictionary Dictionary<string, ISalaryScoreElement> as a field of CalculatorFactory class that will be populated if current object does not exist, and from where the object would be retrieved if it does exist.

Are you actually running out of memory, or something?

Anyway, you can always implement the Singleton pattern for the classes you only wish to create once. The best discussion of how to implement is by Jon Skeet: http://csharpindepth.com/Articles/General/Singleton.aspx

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top