DEV Community

Discussion on: How to avoid the Visitor pattern in C#

Collapse
 
wiltonhotz profile image
WiltonHotz • Edited

Hi! So grateful that you replied!
For some reason I'm still having a hard time extending the types of the example you provided, although I really like the example. Very clear and instructive.

I did something similar myself, with the goal of being able to extend types selectively, and I would love to hear your comments on the approach (heavily using the code you originally provided)

Especially if you have any thoughts on the downside of using this approach :)

What I think I've done is extensible types, extensible functionality, and it's all up to the caller to decide what to call. As well as there's only one generic method to get whatever we're interested in, from the objects that can provide it.

using System;
using System.Collections.Generic;
using System.Linq;

namespace SumWithoutVisitor3
{
    class Program
    {
        static void Main(string[] args)
        {
            var rectangle = new Rectangle(10, 20);
            var triangle = new Triangle(10, 20, "A mighty fine triangle!");
            var circle = new Circle(10);

            var areas = GetAnything<IShape, double, ICanDoSomeShapeCalculations<IShapeCalculationContract>>
                ((ICanDoSomeShapeCalculations<IShapeCalculationContract> x) => x.GetArea(), rectangle, triangle, circle);

            var circumferences = GetAnything<IShape, double, ICanDoSomeMoreShapeCalculations<IShapeCalculationContract>>
                ((ICanDoSomeMoreShapeCalculations<IShapeCalculationContract> x) => x.GetCircumference(), rectangle, triangle, circle);

            var descriptions = GetAnything<IShape, string, ICanDoSomeShapePrinting<IShapePrintingContract>>
                ((ICanDoSomeShapePrinting<IShapePrintingContract> x) => x.GetDescription(), rectangle, triangle, circle);

            Console.WriteLine("Areas");
            foreach (var a in areas)
            {
                Console.WriteLine(a.Item1 + " : " + a.Item2);
            }

            Console.WriteLine("Circumferences");
            foreach (var c in circumferences)
            {
                Console.WriteLine(c.Item1 + " : " + c.Item2);
            }

            Console.WriteLine("Descriptions");
            foreach (var d in descriptions)
            {
                Console.WriteLine(d.Item1 + " : " + d.Item2);
            }
        }
        public static IEnumerable<(string, R)> GetAnything<T,R,U>(Func<U, R> map, params T[] items)
        {
            var anything = items.OfType<U>().Select(x => (x.GetType().ToString(), map(x)));
            return anything;
        }
    }
    interface IShape
    {

    }

    interface IShapeCalculationContract
    {
        double Calculate(Func<double> calculate);
    }
    // add new functionality
    interface IShapePrintingContract
    {
        string Print(Func<string> print);
    }
    class Shape : IShapeCalculationContract, IShapePrintingContract
    {
        public double Calculate(Func<double> calculate) => calculate();
        public string Print(Func<string> print) => print();
    }
    interface ICanDoSomeShapeCalculations<T> : IShape
    {
        double GetArea();
    }
    interface ICanDoSomeShapePrinting<T> : IShape
    {
        string GetDescription();
    }
    class Rectangle : ICanDoSomeShapeCalculations<IShapeCalculationContract>
    {
        public double Width { get; }
        public double Height { get; }
        public Rectangle(double width, double height)
        {
            Width = width;
            Height = height;
        }
        public double GetArea() => new Shape().Calculate(() => Width * Height);
    }
    class Triangle : ICanDoSomeShapeCalculations<IShapeCalculationContract>, ICanDoSomeShapePrinting<IShapePrintingContract>
    {
        public string Description { get; }
        public double Width { get; }
        public double Height { get; }
        public Triangle(double width, double height, string description)
        {
            Description = description;
            Width = width;
            Height = height;
        }

        public double GetArea() => new Shape().Calculate(() => (Width * Height) / 2);
        public string GetDescription() => new Shape().Print(() => Description);
    }
    // add new method 
    interface ICanDoSomeMoreShapeCalculations<T> : ICanDoSomeShapeCalculations<T>
    {
        double GetCircumference();
    }
    // add new type
    class Circle : ICanDoSomeMoreShapeCalculations<IShapeCalculationContract>
    {
        public double Radius { get; }
        public double PI { get; }
        public Circle(double a)
        {
            Radius = a;
            PI = Math.PI;
        }

        public double GetArea() => new Shape().Calculate(() => Radius * Radius * PI);
        public double GetCircumference() => new Shape().Calculate(() => 2 * Radius * PI);
    }
}

Enter fullscreen mode Exit fullscreen mode

Output:
Areas
SumWithoutVisitor3.Rectangle : 200
SumWithoutVisitor3.Triangle : 100
SumWithoutVisitor3.Circle : 314,1592653589793
Circumferences
SumWithoutVisitor3.Circle : 62,83185307179586
Descriptions
SumWithoutVisitor3.Triangle : A mighty fine triangle!