简体   繁体   中英

Are (a lot of) internal methods avoidable? (interface, factory pattern)

I'm currently working on a bot and have about 8 seperate classes for different dialogs (all in the same namespace!). They all contain different Tasks and because of this I have ran into a little problem. I'm wondering what the best practice would be: using an interface, using the factory pattern … All of these options, however, force me to use internal methods. (And I get that with an interface because you promise certain behavior, but with the factory pattern I don't really know - to be honest..) Because there is no definition for the specific methods - but this means I would have to make a lot of these internal methods and I'm trying to avoid redundancy.

First I just instantiated a new object, but I soon realised that this meant a new object of every kind/dialog was made everytime a call was made to the bot - which isn't very efficient right? I have also tried to instanciate them in the constructor, but this forces me to use an interface and this gives me the same problem as I've stated before. I've also looked into partial classes but I'm not sure if using 8 partial classes is really.. okay?

Right now I'm trying out the factory pattern with this code: (Credits to this thread: How to prevent an instantiation of an object in c# )

public class DialogFactory
    {
        private NameDialog _nameDialog;
        private CertificateDialog _certificateDialog;
        private ProfileDialog _profileDialog;
        private ClassDialog _classDialog;
        private LocationDialog _locationDialog;
        private SkillDialog _skillDialog;
        private EducationDialog _educationDialog;
        private SpecializationDialog _specializationDialog;

        public DialogFactory CreateDialog(string dialog)
        {
            switch (dialog.ToLower())
            {
                case "name": return new NameDialog();
                case "certificate": return new CertificateDialog();
                case "profile": return new ProfileDialog();
                case "class": return new ClassDialog();
                case "location": return new LocationDialog();
                case "skill": return new SkillDialog();
                case "education": return new EducationDialog();
                case "specialization": return new SpecializationDialog();
                default: throw new Exception("That dialog does not exist.");
            }

            throw new Exception("That dialog does not exist.");
        }
    }

To give some context of what the dialogs seem like I will add the name dialog here as well:

public class NameDialog : DialogFactory
    {
        ProfileService profileService = new ProfileService();

        public async Task AddNameResponse(ITurnContext turnContext, Profile profile, string value { … }
     }

I try to access the AddNameResponse Task in the main method as follows: await dialog.CreateDialog("name").AddNameResponse(turnContext, profile, value); This is not accepted however giving me the following warning: 'DialogFactory' does not contain a definition for 'AddNameResponse' and no accessible extension method 'AddNameResponse' accepting a frist argument for 'DialogFactory'. The fix would be an internal Task, but I'm trying to avoid this (reason for this given earlier).

I'm really at a loss here because I don't know what the best practice would be. I'm trying to make the most efficient and clean code that I can make and avoid redundancy and use as much loose coupling as possible - but I have no idea how to make that happen in this situation… I hope I've formulated my problem well enough (and also the question)!

The factory pattern makes sense for polymorphism. That means that, for example, we want an IDialog and we don't care what the implementation is. We don't want to know. That way our code depends on IDialog and isn't coupled to any particular class that implements it.

If you're trying to do this:

dialog.CreateDialog("name").AddNameResponse(turnContext, profile, value);

...and the error is that whatever gets returned from dialog.CreateDialog("name") doesn't have a AddNameResponse method, that reveals that your code depends on some more specific class than what is returned from the factory.

The factory won't reduce coupling for a few reasons:

  • Your code still depends on NameDialog . You need that exact class with its AddNameResponse method.
  • Even if the factory returned that exact class, now you're coupled to the factory and that class.

It makes sense that you'd want to reduce the coupling, because if a class is tied to NameDialog it's also tied to ProfileService . It's impossible to test a class that depends on NameDialog without also depending on ProfileService .

The potential solutions will involve changing classes that depend on NameDialog . Here are few thoughts:

  • Define an abstraction (such as an interface or a delegate) that describes what your class needs to do with NameDialog . Inject that into your class. Now your class depends on the abstraction, not the concrete class.
  • If NameDialog does something really simple, perhaps you can inject it instead of an abstraction, and a better fix is to define an abstraction that represents ProfileService and inject that into NameDialog .
  • Possibly do both.

What each of these mean is that you're

  • Avoiding coupling by depending on abstractions
  • Giving your dependency injection/IoC container the responsibility of creating objects

That will work better than combining creation of all those objects into a single factory. That approach would only make sense if any object returned by the factory is substitutable for any other type - you don't need to know what the concrete type is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM