1

I´m looking for the best way to solve following problem:

I have three lists of different objects: AppleJuice, OrangeJuice & Juice. All Juice properties exists in AppleJuice & OrangeJuice and I only need these from AppleJuice & OrangeJuice. I can´t change the AppleJuice & OrangeJuice implementations since they come from two unrelated SOAP services. My aim is merge these 3 lists in a Juice lists and then remove the duplicates using a concrete match pattern.

Which pattern would best to merge these different objects?

I was thinking of using the Facade pattern for the different objects and then Strategy for the match pattern. If it helps, I´ll be using C# to implement the code.

Here what I did.

First, I've extended the Juice class and I've created 3 extensions methods for the classes: AppleJuice, OrangeJuice and Juice.

public class JuiceExtended : Juice
{
    public JuiceSourceType JuiceSource { get; set; }
}

public enum JuiceSourceType 
{
    Apple,
    Orange,
    Juice,
    JuiceWithoutSugar
}

I need to know the source, because if there is a duplicate I have to keep the objects from the Juice

    public static IEnumerable<JuiceExtended> ConvertToExtended(this IEnumerable<AppleJuice> juices)
    {
        var list = juices.Select((x) => {
            var item = new JuiceExtended()
            {
                JuiceSource = JuiceSourceType.Apple,
                //More properties                  
            };
            return item;
        });

        return list;
    }

    public static IEnumerable<JuiceExtended> ConvertToExtended(this IEnumerable<OrangeJuice> juices)
    {
        var list = juices.Select((x) => {
            var item = new JuiceExtended()
            {
                JuiceSource = JuiceSourceType.Orange,
                //More properties
            };
            return item;
        });

        return list;
    }

    public static IEnumerable<JuiceExtended> ConvertToExtended(this IEnumerable<Juice> juices, JuiceSourceType type)
    {
        var list = juices.Select((x) => {
            var item = (JuiceExtended) x;
            item.JuiceSource = type;     
            return item;
        });

        return list;
    }

After this, I've created a JuiceHandler and I've implemented the strategy pattern inside for the dedup algorithm

public class JuiceHandler : IJuiceHandler
{
    private static IEnumerable<JuiceExtended> _juices;
    private JuiceStrategy _strategy;

    public JuiceHandler()
    {
        _juices = new List<JuiceExtended>();
    }

    public JuiceHandler(JuiceStrategy strategy)
    {
        _juices = new List<JuiceExtended>();
        _strategy = strategy;
    }

    public void Load(IEnumerable<JuiceExtended> juices)
    {
        _juices = _juices.Concat(juices);
    }

    public void SetJuiceStrategy(JuiceStrategy strategy)
    {
        _strategy = strategy;
    }


    public IEnumerable<Juice> Transform()
    {
        if (_strategy == null)
            throw new NullReferenceException("The strategy has not been selected.");

        else if (_juices.Count() == 0)
            return _juices;

        return _strategy.Dedup(_juices);
    }

}

Looking about below responses, @MainMa, I think I'll move the extension methods in the WCF partial classes, because I already have partial classes for WCF so, there is no point to put them into another location.

  • Don't put comments in your questions. The person you're addressing (@user2023861) will never get notified. – Robert Harvey May 31 '16 at 16:45
1

You can use the Facade pattern like you thought to treat your AppleJuice and OrangeJuice objects like Juice objects. It's pretty simple. Here's your Juice class:

class Juice
{
    Juice(AppleJuice aj)
    {
        this.Color = aj.Color;
        this.VitaminC = aj.VitaminC;
    }

    Juice(OrangeJuice oj)
    {
        this.Color = oj.Color;
        this.VitaminC = oj.VitaminC;
    }

    public ColorType { get; set; }
    public VitaminCType VitaminC { get; set; }
}

This Juice class covers both juice types and extracts the information that you are interested in. To handle Juice equality, you need to implement IEqualityComparer<Juice>:

class JuiceEq : IEqualityComparer<Juice>
{
    public bool Equals(Juice x, Juice y)
    {
        if (x == null) return y == null;
        if (y == null) return false;
        return x.Color == y.Color && x.VitaminC = y.VitaminC;
    }

    public int GetHashCode(Juice obj)
    {
        if(obj == null) return 0;
        return new { obj.Color, obj.VitaminC }.GetHashCode();
    }
}

Now, to use this when you make you SOAP requests and then remove duplicates:

IEnumerable<Juice> GetJuice()
{
    IEnumerable<AppleJuice> appleJuices = SoapAppleJuiceRequestRequest();
    IEnumerable<OrangeJuice> orangeJuices = SoapOrangeJuiceRequestRequest();

    IEnumerable<Juice> juices = appleJuices.Select(s => new Juice(s))
      .Concat(orangeJuices.Select(s => new Juice(s));

    IEnumerable<Juice> deDuped = juices.Distinct(new JuiceEq());
    return deDuped;
}
  • What it would be better? Merge the list before or after remove the duplicates? – RCalaf May 31 '16 at 16:48
  • I think the code will be easier to read if you remove duplicates after merging the lists. That way you're only calling Distinct once. If you run the Distincts before the merge, you'll probably see better performance because (1) you're calling Distinct on smaller lists and (2) I assume a given AppleJuice object is never equal to any OrangeJuice object and vice-versa. It's your call. – user2023861 May 31 '16 at 17:37
  • 2
    Not to be nit-picky but you and the OP are twisting the meaning and intent of the "Facade Pattern". "A facade is an object that provides a simplified interface to a larger body of code, such as a class library." Generally a facade is used as an API wrapper around some third party library or application API. In that sense it is more of a high level architectural pattern where you are essentially rewriting the 3rd party API to be more friendly to your application code. I don't really see a reason for specifying a particular pattern in this case but Adapter is probably more cogent if you must. – Dunk May 31 '16 at 18:34
  • @Dunk, Thanks for the explanation. I was unsure if I should call it an Adapter or a Facade. I wonder if it even needs a name. It's just a constructor that copies members of another type. – user2023861 May 31 '16 at 18:55
  • I don't think it needs a name because it is simply doing type conversions but the closest pattern I see that applies is the adapter pattern, which seems like overkill to implement "according to the official description" for this particular case. "The adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface." Other than that, your implementation seems quite reasonable and doesn't really match any pattern that I am aware of. – Dunk May 31 '16 at 19:15
0

There is no need for any specific pattern here.

While you can't change the properties within the dynamically generated classes created from SOAP service endpoints, you can add behavior to the types, the generated classes being partial.

This makes it possible to create a method which converts AppleJuice or OrangeJuice to Juice:

// Make sure the namespace matches with the actual namespace used by the partial class.
namespace ServiceReference1
{
    public partial class AppleJuice
    {
        public Juice ToJuice()
        {
            ...
        }
    }
}

...

public Juice GetAppleJuice()
{
    var client = new AppleServiceClient();
    var appleJuice = client.GetSomeAppleJuice();
    return appleJuice.ToJuice();
}

From this moment on, you can work with the instances of Juice only (including storing them in the same typed collection).

If you need to call the service and send it back an AppleJuice or OrangeJuice, then you can do the conversion the other way:

var juice = new Juice(...);
var client = new AppleServiceClient();
var appleJuice = new AppleJuice(juice);
client.DrinkJuice(appleJuice);
  • I think use partial classes is a good spot to have the code tiddier – RCalaf May 31 '16 at 16:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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