Całkiem ciekawe, chociaż rozwiązałem to inaczej:
- Zasada działania opiera się o dość okrojone dependency injection (przycięte do problemu).
Mianowicie mamy klasę ze słownikiem od danego enuma (dla przykładu z góry od Fun) oraz Type, a w niej publiczny indekser zwracający obiekt typu w zależności od wartości podanego enuma oraz oraz internal metodę dodającą wartości do tego słownika.
- Z racji, że nie możemy do tego wykorzystać interfejsów, więc tworzymy publiczną klasę abstrakcyjną np. AbstractFun, w której jest owy publiczny enum z możliwymi funkcjiami, czyli np. FunA, FunB, FunC; następnie jest delegat internal z opisem tej metody, a następnie obiekt delegata, do wywoływania (również internal)
- Następnie implementacja metod - wszystkie dziedziczą oczywiście po AbstractFun (np. internal class FunA : AbstractFun). Do delegata z klasy nadrzędnej przypisujemy odpowiednią implementację danej metody.
Oczywiście ideę opisałem w miarę prosto, ale ja to jeszcze zrobiłem generycznie, co mi daje np. takie możliwości (teraz przykład bardziej konkretny)
Tworzę sobie właściwość public Całkowanie Całkowanie { get; set; } oraz np. public Analizator<Całkowanie, Całkowanie.Metody> MetodyCałkowania {get;}
Potem oczywiście tworzę obiekt analizatora i rejestruję metody
MetodyCałkowania.Rejestruj<Kwadraty>(Całkowanie.Metody.Kwadraty)
MetodyCałkowania.Rejestruj<Trapezy>(Całkowanie.Metody.Trapezy)
W metodzie, w której będę chciał to wywołać robię Całkowanie.Wywołaj(/*jakieś argumenty zgodne z delegatem*/)
A wykorzystanie jest następujące:
var api = new API();
api.Całkowanie = api.MetodyCałkowania[Całkowanie.Metody.Trapezy];
api.Licz(); //metoda w której ma być to wykonanie.
Dodanie metody sprowadza się raptem do napisania jej implementacji oraz zarejestrowania jej w ,Analizatorze", no… dobra, dodania jeszcze w abstrakcyjnej klasie wartości do enuma.
Generyczne wykonanie daje natomiast jeszcze jeden plus - jak będę chciał mieć kolejną grupę metod, to dodać jest banalnie łatwo, np. jakbym chciał również na różne sposoby rozwiązywać jakieś macierze, to robię tylko klasę abstrakcyjną, do niej klasy implementacji metod (np. Gauss itp.) a w klasie api dodaję tylko obiekt Analizatora od np. Macierze i od Macierze.Metody i analogicznie jak przy całkach.
Mam nadzieję, że w miarę zrozumiale opisałem ideę, nie chciałem konkretami rzucać, bo to do pracy inżynierskiej, a potem bym się musiał tłumaczyć, że to nie plagiat, bo to ja sam napisałem.
Ma to sporo plusów - jest uniwersalne, mogę wiele ,typów" metod przypisywać z zewnątrz do wykonania (np. właśnie metody całkowania czy jakichś wyliczeń na macierzach), łatwo dopisuje się kolejne metody, nie ma ,brzydkiego" kodu we właściwej metodzie (ify, switche - 3 switche, każdy po 10 case’ów); swoją drogą to wgl. nigdzie nie ma ,brzydkiego kodu", oczywiście cel jest osiągnięty - bo z zewnątrz można przypisać właściwą metodę, ale nie można jej wywołać. Ale ma też jeden minus - przy dodawaniu implementacji kolejnej metody trzeba pamiętać, że oprócz jej implementacji, trzeba jeszcze w klasie nadrzędnej dodać wartość enuma, oraz zarejestrować ją w klasie api.
Mam nadzieję, że się spodoba, jeśli jednak ma ktoś pomysł jakby to ulepszyć, albo zupełnie inaczej zrobić - ale lepiej (żeby cel był osiągnięty, ale dodanie metody wymagało mniej pracy) - to niech się podzieli, będę wdzięczny.
//EDIT
Jak się tak jeszcze nad tym zastanowić, to niewielkim nakładem pracy da się ten przykład poprawić i wyeliminować enum. Wówczas przy dodawaniu nowej metody wystarczy tylko jej implementacja i rejestracja w konstruktorze
Pozdrawiam.