Слишком много, если (obj is thisObj) утверждения

В настоящее время у меня есть метод, который пытается выяснить, что obj получает. Он знает, что на определенном интерфейсе, например IService, но у меня есть код, который смотрит на него и пытается сказать мне, что это, например, Service1 или Service2. Я в настоящее время много примеров if (obj isObj), что было бы лучшим решением, чтобы сделать этот код довольно?

вот пример того, что у меня есть:

    public void DoSomething(IService service)
    {
        if (service is Service1)
        {
            //DO something
        }

        if (service is Service2)
        {
            //DO something else
        }           
    }

теперь есть два isnt слишком много плохой вещи, но я смотрю на наличие, вероятно, 20+ из них, которые просто становятся ужасно использовать.

Любые идеи?


ok дальнейшие подробности, я думаю, нужны, и вот они:

до этого метода у меня есть другой метод, который получает документ xml, который он десериализует в интерфейс IService, поэтому мы имеем что-то вроде этого:

    private static void Method(InnerXml)

    {

            var messageObj = (IServiceTask)XmlSerialization.Deserialize(typeof(IServiceTask), InnerXml);

            var service = GetService(messageObj);
            service.PerformTask(xmlDoc);

    }

    private static IService GetService(IServiceTask messageObj)
    {
        var service = new IService ();

        if (messageObj is Task1)
        {
            service = (SomeService)messageObj;
        }

        if (messageObj is Task2)
        {
            service = (SomeOtherService)messageObj;
        }
        return service ;
    }

Надеюсь, это немного упростит.

Ответы

Ответ 1

Я считаю, что вы хотите:

class ServiceFactory 
{
     Dictionary<Type, NewService> serviceCreators;
     ServiceFactory() 
     {
         serviceCreators = new Dictionary<Type, NewService>();
         serviceCreators.Add(typeof(Task1), delegate { return new SomeService(); });
         serviceCreators.Add(typeof(Task2), delegate { return new SomeOtherService(); });
     }

     public IService CreateService(IServiceTask messageObj) 
     {
         if(serviceCreators.Contains(messageObj.GetType()) 
         {
              return serviceCreators[messageObj.GetType()];
         }
         return new DefaultService();
     }
}

delegate IService NewService();

Или, может быть, добавить новый метод к IServiceTask - CreateService.

Ответ 2

Можете ли вы изменить IService?

Добавьте метод DoSomething() и реализуйте его во всех сервисах.

Ответ 3

Ну, это зависит от того, что делают линии //DO something. В некоторых случаях было бы целесообразно объявить метод в интерфейсе службы и поместить логику для этих операций в самих сервисах.

Иногда, с другой стороны, это код, который сама служба не должна знать, - в этот момент жизнь становится отчетливо уродливой: (Иногда такого рода вещи очень трудно избежать. дженериков и лямбда-выражений, например

ConditionallyExecute<Service1>(service, s1 => s1.CallSomeService1Method());
ConditionallyExecute<Service2>(service, s2 => s2.CallSomeService2Method());
...

где ConditionallyExecute есть что-то вроде:

private void ConditionallyExecute<T>(object obj, Action<T> action)
    where T : class
{
    T t = obj as T;
    if (t != null)
    {
       action(t);
    }
}

... но я не очень счастлив, когда я это делаю: (

Ответ 4

Мне нравится использовать словарь в этих сценариях.

Dictionary<Type,Action<IService>>

Ответ 5

Что касается меня - я бы действительно пошел с помощью метода doSomething() на интерфейсе, чтобы вы могли реализовать его во всех этих классах. У вас было бы:

public void DoSomething(IService service)
{

    service.doSomething();
}

Ответ 6

Это не делает его лучшим чтением, но, возможно, более эффективным (если служба не может быть одновременно двух типов):

    public void DoSomething(IService service)
    {
        if (service is Service1)
        {
            //DO something
        }
        else if (service is Service2)
        {
            //DO something else
        }           
    }

Другой подход

Возможно, это было бы также возможным решением:

private Dictionary<Type, Action<object>> _TypeExecutor;

private void SetupExecutors()
{
    _TypeExecutor = new Dictionary<Type, Action<object>>();

    _TypeExecutor.Add(typeof(Service1), new Action<object>((target) => target.DoSomething()));
    _TypeExecutor.Add(typeof(Service2), new Action<object>((target) =>
        {
            var instance = (Service2)target;
            var result = instance.DoSomething();
        }));
    _TypeExecutor.Add(typeof(Service3), AnotherMethod);

}

private void AnotherMethod(object target)
{
    var instance = (Service3)target;
    var result = instance.DoSomething();
}

private void DoWork(ISomething something)
{
    Action<object> action;

    if (_TypeExecutor.TryGetValue(something.GetType(), out action))
    {
        action(something);
    }
}

Ответ 7

Вообще говоря, если вы думаете, что должны делать что-то вроде своего кода, это сильный признак того, что в вашем дизайне что-то не так. Если вы передаете этот метод IService, то в идеале намерение должно состоять в том, что он хочет вызвать метод на этом интерфейсе - не заботясь о том, какая реализация стоит!

Но кроме этого. может быть полезно в вашем случае иметь какое-то свойство Servicetype на вашем интерфейсе IService (в идеале это вернет значение перечисления), которое вы могли бы проверить с помощью оператора switch. Это, конечно, не уменьшило бы необходимое количество логических ветвей (вы не сможете уменьшить его без реорганизации своей архитектуры), но по крайней мере это значительно уменьшит необходимый объем кода.

Томас

Ответ 8

Если функциональность не принадлежит правильно в IService, то либо Will Шаблон команды и карту какого-либо типа или с помощью шаблона .

Последнее требует, чтобы вы добавили новый метод IService.Visit и создали интерфейс IServiceVisitor с помощью методов Visit(Service1) и Visit(Service2) (и т.д.).

Пример:

interface IService 
{
    void Visit(IServiceVisitor visitor);
}

class Service1 : IService
{
    void Visit(IServiceVisitor visitor) 
    {
        visitor.Visit(this);
    }
}


class Service2 : IService
{
    void Visit(IServiceVisitor visitor) 
    {
        visitor.Visit(this);
    }
}

interface IServiceVisitor 
{
    void Visit(Service1 service);
    void Visit(Service2 service);
}

class ClassThatDoesStuff : IServiceVisitor 
{
{
    void Visit(Service1 service) 
    {
         // Service one code
    }
    void Visit(Service2 service) 
    {
         // Service two code
    }

    public void DoSomething(IService service) 
    {
         serivce.Visit(this);
    }
}

Ответ 9

используйте полиморфизм, это очень простое решение.

class Abstract
{
  function something();
}

class A inherit Abstract
{
  override something()
}

class B inherit Abstract
{
  override something()
}


function foo (Abstract input)
{
 input->something()
}

Ответ 10

Предполагая, что вы хотите выполнить определенный метод в соответствии с фактическим типом, вы можете использовать GetMethod в экземпляре, и если метод существует, вызовите его.

public void DoSomething(IService service)
{
    System.Reflection.MethodInfo method = service.GetType().GetMethod("MySpecialMethod");
    if (method != null)
        method.Invoke(service, null);
}

Таким образом, вам не нужно будет проверять тип вообще, просто проверьте, существует ли этот метод - вроде ходьбы по дереву, поэтому я надеюсь, что этот подход будет полезен.

Вы также можете использовать массив возможных методов и итерации по ним, проверять каждый и иметь более элегантный код таким образом.

Ответ 11

Как и другие, самое легкое решение для этой логики было бы сделано внутри ваших реализаций IService через добавленный метод.

Если эта функциональность действительно не принадлежит внутри IService, шаблон Visitor будет гораздо лучшим решением, чем большое количество проверок instanceof.

Вы создали бы такой интерфейс, как

public interface IServiceHandler {
    void handleService1(Service1 s);
    void handleService2(Service2 s);
    // add more methods for every existing subclass of IService
}

С реализацией, которая обрабатывает логику в настоящее время внутри DoSomething, но с каждой ветвью, разделенной на нее собственным методом:

public class ServiceHandler : IServiceHandler {
    public void handleService1(Service1 s) { ... }
    public void handleService2(Service2 s) { ... }
}

Для этого IService понадобится один метод добавления:

void accept(IServiceHandler sh);

который будет реализован в конкретных реализациях, таких как

public class Service1 : IService {
    ...
    public void accept(IServiceHandler sh) { sh.handleService1(this); }
    ....
}

и аналогично для других реализаций.

Затем ваш оригинальный метод DoSomething() можно переписать как

public void DoSomething(IService service) {
    service.accept(new ServiceHandler());
}

Преимущество такого подхода заключается в том, что ваша логика будет намного лучше разделена, а также немного более результативной, поскольку она больше не использует какие-либо проверки экземпляров или отбрасывает.

Кроме того, если вы когда-либо добавляете новую реализацию IService, компилятор заставит вас добавить к ней соответствующий обработчик (так как ему нужно реализовать метод accept(), который может быть выполнен только путем добавления соответствующего случая к IServiceHandler), тогда как при решении, зависящем от ряда проверок типов, было бы легко забыть добавить соответствующий дополнительный случай.

И последнее, но не менее важное: если вам когда-либо понадобились другие типы обработчиков, вы могли бы сделать это без каких-либо дальнейших изменений в IService; вы просто создали бы новую реализацию IServiceHandler с новой логикой.

Ответ 12

Если вы сами пишете классы Service, интерфейсы - это путь. Если Foo() должен быть вызван на объект, если он либо Service1, либо Service2, то они должны реализовать общий интерфейс, и вы просто проверяете, является ли его одним из двух, а затем запускает соответствующий код.

Если их классы не могут быть изменены, тогда я думаю, что вам не повезло. 20+ совершенно разные классы, которые должны иметь 20 + совершенно разные наборы логики, применяемые к ним, должны просто... обрабатываться по-разному.

Или я пропустил здесь магию С#? Каждый раз, когда я вижу такой код, я думаю о том, как интерфейсы реализации Go.