我正在寻找一种为接口提供单个入口点的技术,但是每个实现的处理方式都不同。

让我们来看一个例子。

我有几个Instrument接口的实现。当然,乐器具有一些相似之处(它们制作音乐,与音符和音阶有关),但演奏方式却大不相同。

Musician可以演奏乐器,有天赋的音乐家可以演奏几种乐器:

public interface Musician {
    void play(Instrument instrument);
}
public class GiftedMusician implements Musician {

    @Override
    public void play(Instrument instrument) {
        if (instrument instanceof Guitar) {
            play((Guitar) instrument);
        } else if (instrument instanceof Bass) {
            play((Bass) instrument);
        } else if (instrument instanceof Piano) {
            play((Piano) instrument);
        }
    }

    public void play(Guitar guitar) {
        guitar.strumWithPick();
    }
    public void play(Bass bass) {
        bass.pluckString();
    }
    public void play(Piano piano) {
        piano.pressKey();
    }
}


我已经找到了使用instanceof的解决方案,但是我不确定这是否可行。我正在寻找一种设计模式或其他最佳实践来处理这种情况。

编辑:
这个例子当然很简单,让我们使其不那么明显。因为,正如我所说,有许多种乐器以不同的方式演奏。像个低音提琴我如何实现一个演奏普通和低音提琴的Musician

public class Contrabass implements Instrument{
    public void play(boolean useBow) {
         if(useBow)
             playWithBow();
         else
             pluckWithFingers();
    }
}

最佳答案

我认为,您应该在Instrument中声明以下方法:

public void play(Musician musician);


然后,您可以为每种仪器实现不同的实现。

例如:

class Guitar implements Instrument {
    @Override
    public void play(Musician musician) {
        System.out.printf("Musician %s is playing the guitar!%n", musician.getName());
        strumWithPick();
    }
}


... 等等。

在此示例中,除非您决定使用composition将GiftedMusician或多个InstrumentMusician关联,否则您的GiftedMusician类没有什么意义。

在后一种情况下,您的Collection<Instrument>将具有一个构造函数重载,例如,一个Musician,而您的Instrument仅将具有单个Instrument的构造函数。

例如(将play作为抽象类,将核心“功能”添加到play):

class Musician {
    protected Collection<Instrument> instruments;
    Musician(Instrument instrument) {
        instruments = new HashSet<Instrument>();
        if (instrument != null)
            instruments.add(instrument);
    }
    public String getName() {
        // of course
        return "J. S. Bach";
    }
}

class GiftedMusician extends Musician {
    GiftedMusician(Instrument instrument) {
        super(instrument);
    }
    GiftedMusician(Collection<Instrument> instruments) {
        super(null);
        this.instruments = new HashSet<Instrument>(instruments);
    }
}
abstract class Instrument {
    protected String name;
    public void play(Musician musician) {
        System.out.printf("Musician %s is playing %s%n", musician.getName(), name);
    }
}


编辑后续问题编辑。

如果您需要在instanceof方法中参数化特定的播放技术,而无需重载反模式并返回到play长列表反模式,则您更有理由对参数Musician进行参数化Musician

毕竟,由play决定他们想要使用的技术。

一旦进入musician.getCurrentTechnique()正文,您就可以使播放逻辑适应行中的内容。

10-07 20:02