设计建议,未能将工厂与仿制药结合起来

时间:2014-12-11 22:33:48

标签: java design-patterns

我在使用Java设计时遇到以下问题:

我需要创建一个吉他店,为不同的吉他提供调音服务。 每种吉他类型的调音都有所不同。

我需要提供一个设计(类和关系),可以处理需要调整吉他的基本场景。

所以我得到了:

  • Guitar abstract \ interface
  • 2吉他实现(Gibson和Fender)
  • 我拿到了我的GuitarShop
  • GuitarService(interface \ abstract)
  • GibsonService(实现GuitarService)
  • FenderService(实现GuitarService)
  • GuitarService工厂

enter image description here 所以基本上工厂获得吉他类型并返回所需的吉他服务。但吉他服务包含一种方法:

public void tuneGuitar(Guitar guitarToTune) {...}

这意味着我需要在每项服务中每次都投出我的吉他(每首吉他的调音都不同)。

我试着通过模板化GuitarService来使用泛型 但是我的工厂不能再为我服务了(我无法安全地实例化运行时确定参数的对象)。

这种情况是否有最佳做法?

4 个答案:

答案 0 :(得分:2)

主要问题是您的Guitar界面完全隐藏了工厂用于选择相应GuitarTuningService实施所需的详细信息。解决问题的一种方法是实现访客模式,a.k.a。双重调度。 Guitar接口将获得以下方法:

public void accept(GuitarVisitor visitor);

每个Guitar实现将分别以相同的方式实现该方法:

public void accept(GuitarVisitor visitor) {
    visitor.visit(this);
}

GuitarVisitor界面如下所示:

public interface GuitarVisitor {
    public void visit(Fender guitar);
    public void visit(Gibson guitar);
    // other Guitar types as necessary
}

GuitarVisitor的实现在每种方法中执行适当的吉他类型特定行为。例如,GuitarTuningServiceFactory可以使用内部GuitarVisitor实现为每个提供的吉他选择合适的GuitarTuningService。您无法避免强制转换(尽管您可以通过Class.cast()执行它来避免类型安全警告),但您可以在每个tune()方法的开头只安全地执行一次,记录向下转发参考。

Visitor模式的主要缺点是它锁定了特定访问类的可能类型。您可以添加一个catch-all方法来部分缓解(public void visit(Guitar guitar))。

答案 1 :(得分:1)

下面我提供了您要求的解决方案。但我不确定,如果这是您真正需要解决问题的解决方案。因为我无法理解,为什么你需要按类类型对不同吉他的属性进行建模。但我只能假设。

方法1 |每个新服务的转换+重新编译

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class Main {

    public static void main(String[] args) {
        Guitar f = new Fender();
        Guitar g = new Gibson();

        TuneService<Guitar> fts = TuneServices.getService(f);
        fts.tune(f);

        TuneService<Guitar> gts = TuneServices.getService(g);
        gts.tune(g);
    }

    static interface Guitar {
    }

    static class Fender implements Guitar {
    }

    static class Gibson implements Guitar {
    }

    // tune service that accepts only guitars as generic types
    static interface TuneService<T extends Guitar> {
        public void tune(T guitar);
    }

    static class FenderService implements TuneService<Fender> {
        @Override
        public void tune(Fender guitar) {
            System.out.println("Tune Fender.");
        }
    }

    static class GibsonService implements TuneService<Gibson> {
        @Override
        public void tune(Gibson guitar) {
            System.out.println("Tune Gibson.");
        }
    }

    // factory class
    static final class TuneServices {
        private static final Map<Class<?>, TuneService<?>> services;
        static {
            Map<Class<?>, TuneService<?>> tmp = new HashMap<Class<?>, TuneService<?>>();
            tmp.put(Fender.class, new FenderService());
            tmp.put(Gibson.class, new GibsonService());

            // populate services
            services = Collections.unmodifiableMap(tmp);
        }

        public static <T extends Guitar> TuneService<T> getService(T guitar) {
            TuneService<?> s = services.get(guitar.getClass());
            if (s == null)
                throw new IllegalArgumentException();

            // cast is safe at this point
            @SuppressWarnings("unchecked")
            TuneService<T> ts = (TuneService<T>) s;
            return ts;
        }
    }
}

方法2 | ServiceLoader + cast +每个新服务没有重新编译

使用这种方法,您无需在添加新的吉他服务时重新编译工厂, 当你添加一把新吉他时,你甚至可以扩展这种方法以避免重新编译。

<强> ServiceLoaderExample.java

package guitar;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

public class ServiceLoaderExample {

    public static void main(String[] args) {
        Guitar f = new Fender();
        Guitar g = new Gibson();

        TuneService<Guitar> fts = TuneServices.getService(f);
        fts.tune(f);

        TuneService<Guitar> gts = TuneServices.getService(g);
        gts.tune(g);
    }

    static interface Guitar {
    }

    static class Fender implements Guitar {
    }

    static class Gibson implements Guitar {
    }

    // tune service that accepts only guitars as generic types
    static interface TuneService<T extends Guitar> {
        public void tune(T guitar);
    }

    // factory class
    static final class TuneServices {
        private static final Map<Class<?>, TuneServiceProvider<?>> providers;
        static {
            @SuppressWarnings("rawtypes")
            ServiceLoader<TuneServiceProvider> loader = ServiceLoader.load(TuneServiceProvider.class);

            Map<Class<?>, TuneServiceProvider<?>> tmp = new HashMap<Class<?>, TuneServiceProvider<?>>();
            for (TuneServiceProvider<?> s : loader) {
                tmp.put(s.getGuitarType(), s);
            }

            // populate providers
            providers = Collections.unmodifiableMap(tmp);
        }

        public static <T extends Guitar> TuneService<T> getService(T guitar) {
            TuneServiceProvider<?> p = providers.get(guitar.getClass());
            if (p == null)
                throw new IllegalArgumentException();

            TuneService<?> s = p.getService();

            // cast is safe at this point
            @SuppressWarnings("unchecked")
            TuneService<T> ts = (TuneService<T>) s;
            return ts;
        }
    }
}

<强> TuneServiceProvider.java

package guitar;

import guitar.ServiceLoaderExample.Guitar;
import guitar.ServiceLoaderExample.TuneService;

public interface TuneServiceProvider<T extends Guitar> {
    public TuneService<T> getService();

    public Class<T> getGuitarType();
}

<强> FenderTuneServiceProvider.java

package guitar;

import guitar.ServiceLoaderExample.Fender;
import guitar.ServiceLoaderExample.TuneService;
import guitar.TuneServiceProvider;

public class FenderTuneServiceProvider implements TuneServiceProvider<Fender> {

    @Override
    public TuneService<Fender> getService() {
        return new FenderService();
    }

    @Override
    public Class<Fender> getGuitarType() {
        return Fender.class;
    }

    static class FenderService implements TuneService<Fender> {
        @Override
        public void tune(Fender guitar) {
            System.out.println("Tune Fender.");
        }
    }
}

<强> GibsonTuneServiceProvider.java

package guitar;

import guitar.ServiceLoaderExample.Gibson;
import guitar.ServiceLoaderExample.TuneService;
import guitar.TuneServiceProvider;

public class GibsonTuneServiceProvider implements TuneServiceProvider<Gibson> {

    @Override
    public TuneService<Gibson> getService() {
        return new GibsonService();
    }

    @Override
    public Class<Gibson> getGuitarType() {
        return Gibson.class;
    }

    static class GibsonService implements TuneService<Gibson> {
        @Override
        public void tune(Gibson guitar) {
            System.out.println("Tune Gibson.");
        }
    }
}
如果你使用maven,

/META-INF/services/guitar.TuneServiceProvider 必须在classpath或main / java / resources上

guitar.FenderTuneServiceProvider
guitar.GibsonTuneServiceProvider

输出

两种方法的输出相同。

Tune Fender. 
Tune Gibson.

缺点

两种方法都有相同的缺点,如果客户可以访问该服务。

TuneService<Guitar> fts = TuneServices.getService(f);
fts.tune(f);

TuneService<Guitar> gts = TuneServices.getService(g);
gts.tune(g);

// use gibson service with fender
gts.tune(f);

如果服务与超类型ClassCastException一起使用,则可以在运行时抛出Guitar。 FenderService与Gibson一起使用,反之亦然。 为了避免这种情况,服务调用可以封装在一个方法中,获得正确的服务(这个方法已经存在)并将其应用到吉他上。

答案 2 :(得分:0)

你可以做到

public interface GuitarTuningService<T extends Guitar> {

    void tune(T guitar);
}

然后

public class FenderTuningService implements GuitarTuningService<Fender> {

    public void tune(Fender guitar) {
        // do stuff
    }
}

在工厂里

public class GuitarTuningServiceFactory {

    public GuitarTuningService getTuningService(final Guitar guitar) {
        if (guitar instanceof Fender) {
            return fenderTuningService;
        }
        // if guitar is a Gibson, return GibsonTuningService
    }

    private final GuitarTuningService<Fender> fenderTuningService
        = new FenderTuningService();
}

答案 3 :(得分:0)

  

我试图通过模板化GuitarService来使用泛型但是我的工厂不能再为我服务了(我不能安全地实例化在运行时确定参数的对象)。

您可以像这样实现:

// declare a common interface
public interface GuitarTuningService<GuitarGenericType extends Guitar> {
  void tune(GuitarGenericType guitar)
}

public class FenderTuningService implements GuitarTuningService<FenderGuitar> {
  public void tune(FenderGuitar guitar) {
    // tune it
  }
}

工厂反过来可能是抽象的:

public abstract GuitarServiceFactory<T extends GuitarTuningService> {
  T getGuitarService();
}

// and the concrete implementation:
public class FenderServiceFactory extends GuitarServiceFactory<FenderTuningService> {
  public FenderTuningService getGuitarService() {
    // instantiate & return FenderTuningService 
  }
}

最后我们可以使用这样的东西:

public class GuitarTuner {

  private GuitarServiceFactory factory;
  private Guitar guitar;

  public GuitarTuner(Guitar guitar) {
    this.guitar = guitar;
    switch (guitar.getClass()) {
      case Fender.class:
        factory = new FenderServiceFactory();
        break;
      // other guitar types
      throw new IllegalArgumentException("Unknown guitar type added");
    }
  }

  public void tune() {
    factory.tune(guitar);
  }
}