Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which pattern should I break?

Do Not Repeat Yourself or Encapsulation?

Let's say I created the following:

  1. a interface IMask that implements IList.
  2. a Class Spot that implements IMask.
  3. a Class Marker that contains a few integers and a Spot as fields.

I'd like to make Marker implement the IMask interface. But then I'd be REPEATING MYSELF (check the code in the end) Or I could my the Spot within the Marker public. But then I'd be exposing the implementation of my class. Or I could my Spot inherit from Spot, but this is not the ideal solution, since semantically a Marker is not a specific type of Spot.

And what if I create another class that has a Spot as a field AND I want, again, to implement the IMask interface? I would be repeating myself AGAIN. So, how should I proceed? Should I make the List within Spot public? And then make the Spot in Marker public? Or should I repeat the calls?

interface IMask : IList<Point>
    {
        public void MoveTo(Point newCenter);
        // ... other Methods
    }

    public class Spot : IMask
    {
        List<Point> points;

        public void DoSpotyStuff()
        {
            // blabla
        }

        // Other methods
        // ...
        // Finally the implementation of IMask
        public void MoveTo(Point newCenter)
        {
            // blabla
        }

        // And of course the IList methods
        public void Add(Point newPoint)
        {
            points.Add(newPoint);
        }
    }

    public class Marker : IMask
    {
        private Spot mySpot;
        private int blargh;
        // Other fields

        public void MarkeryMethod()
        {
            // Blabla
        }

        // HERE IS THE PROBLEM: Should I do this and repeat myself
        public void MoveTo(Point newCenter) { mySpot.MoveTo(newCenter); }

        // And here I'm REALLY starting to repeat myself
        public void Add(Point newPoint) { mySpot.Add(newPoint); }
    }

Observations: The interface IMask is not inheriting from List. It's implementing the IList interface, which in turn implements ICollection, IEnumerable Assume that Marker is NOT, semantically, special kind of Spot. So, even though I could Inherit from Spot and solve the issue, it would not be the best solution.

like image 330
Trauer Avatar asked Mar 16 '23 00:03

Trauer


2 Answers

An interface is to establish a kind of contract that the class that will implment it will have this methods. So what you can do is an absctract/base class that implements your Interface and then the classes Marker and Spot can inherit from the base class and you are all set and good to go.

public abstract class BaseClass : IMask {...}

public class Marker : BaseClass{...}

public class Spot : BaseClass{...}

UPDATE January 2020 : Within C#8 you can have default interface methods with implementation (which basically kills the use case for abstract classes), you can take a look here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods

like image 97
Coastpear Avatar answered Mar 28 '23 14:03

Coastpear


Seems to me your choice should be based on how you expect the application to grow, i.e. forward compatibility.

If you think Marker is going to evolve, e.g. maybe some day it will contain more than one Spot, or more than one object that supports IMask, then repeating yourself is the way to go, because you're going to want to coordinate the calls to MoveTo and Add for all of the objects contained in the Marker, and you'll be glad there is a layer of indirection in Marker.

If you think Spot is going to evolve, e.g. if Spot is going to have more methods added like ChangeSize or Remove, perhaps the best thing to do is expose the Spot as a public property of Marker with type IMask. That way the new properties are immediately exposed without writing additional wrapper code.

like image 31
John Wu Avatar answered Mar 28 '23 13:03

John Wu