Java 使用带有抽象参数的策略设计模式

Java 使用带有抽象参数的策略设计模式,java,oop,design-patterns,solid-principles,strategy-pattern,Java,Oop,Design Patterns,Solid Principles,Strategy Pattern,我目前正在从事一个非常简单的项目,以提高我的扎实和设计模式知识。 这个想法是为一扇门创造一个“智能锁”,可以通过指纹、面部识别等不同参数识别一个人 我立即看到了使用策略设计模式的潜力,因此我创建了一个锁接口和一个键抽象类: public interface Lock { boolean unlock(Key key); } public abstract class Key { private String id; public String getId(){

我目前正在从事一个非常简单的项目,以提高我的扎实和设计模式知识。 这个想法是为一扇门创造一个“智能锁”,可以通过指纹、面部识别等不同参数识别一个人

我立即看到了使用策略设计模式的潜力,因此我创建了一个接口和一个抽象类:

public interface Lock {
    boolean unlock(Key key);
}

public abstract class Key {
    private String id;

    public String getId(){
        return (this.id);
    }
}
我创建了两个类,它们将扩展Key-FacePhotoFingerPrint

public class FacePhoto extends Key {
}

public class FingerPrint extends Key {
}
public class FacialRecognizer implements Lock {
    @Override
    public boolean unlock(Key key) throws Exception {
        if(key instanceof FacePhoto){
            //check validity
            return true;
        }
        else {
            throw new Exception("This key does not fit this lock");
        }
    }
}

public class FingerPrintRecognizer implements Lock {
    @Override
    public boolean unlock(Key key) throws Exception {
        if(key instanceof FingerPrint){
            //check validity
            return true;
        }
        else {
            throw new Exception("This key does not fit this lock");
        }
    }
}
然后我创建了实现锁定的类,如指纹识别器面部识别器

public class FacePhoto extends Key {
}

public class FingerPrint extends Key {
}
public class FacialRecognizer implements Lock {
    @Override
    public boolean unlock(Key key) throws Exception {
        if(key instanceof FacePhoto){
            //check validity
            return true;
        }
        else {
            throw new Exception("This key does not fit this lock");
        }
    }
}

public class FingerPrintRecognizer implements Lock {
    @Override
    public boolean unlock(Key key) throws Exception {
        if(key instanceof FingerPrint){
            //check validity
            return true;
        }
        else {
            throw new Exception("This key does not fit this lock");
        }
    }
}
我真的找不到更好的方法来处理界面的用户试图用不合适的钥匙打开锁的情况。 此外,“instanceof”if语句也有问题,因为它出现在实现锁的每个类中


在这种情况下,战略是一种良好的做法吗?如果不是,那么什么是一个很好的替代方案(可能是一个不同的设计模式)。

策略模式提供了在运行时更改行为的能力。在您的例子中,Lock的特定具体实现可以与key的特定实现一起工作,因此逻辑不允许行为改变,因此该模式在当前实现中是不匹配的

战略模式示例

 class A{
    private Behavior b; //behavior which is free to change
    public void modifyBehavior(Behavior b){
         this.b = b;
    }
    public  void behave(){
          b.behave(); // there is no constraint of a specific implementation but any implementation of Behavior is allowed.
     }
 }

 class BX implements Behavior {
     public void behave(){
           //BX behavior
     }
 }

 class BY implements Behavior {
     public void behave(){
           //BY behavior
     }
 }

interface Behavior {
      void behave();
}
在您的案例中,您需要重构抽象以更好地适应逻辑


作为重构(不使用当前的策略模式作为强制设计模式使用)是一个坏的实践,目前 L*/Strategy>从Strategy 可以使用特定类型的

钥匙打开

interface Lock<K extends Key> {
    void unlockUsing(K key);
}

interface Key {
    // TODO
}
我们需要某种方法将钥匙分配到正确的锁。如果
钥匙使用
面部照片
,我们希望使用
面部锁

目前,只有
钥匙
知道/决定应该使用哪个锁为什么不让
键决定使用哪个锁?

首先,为了让钥匙决定使用哪个锁,我们需要以某种方式将这些锁传递给钥匙。我们可以将不同的锁隐藏在门面后面,并将其传递给

class Door {
    private LockSet locks;

    public void unlockUsing(Key key) {
        key.unlock(locks); // the key will decide!
    }
}

interface Key {
    void unlock(LockSet locks);
}

class LockSet {
    private Lock<FacePhoto> faceLock;
    private Lock<FingerPrint> printLock;

    public void unlockUsing(FacePhoto photo) {
        faceLock.unlockUsing(photo);
    }

    public void unlockUsing(FingerPrint print) {
        printLock.unlockUsing(print);
    }
}
您不能使用错误的钥匙和错误的锁。所有可能的锁都是通过
锁集指定的。由于
锁集
公开了类型安全接口,因此您无法尝试使用
指纹打开
,编译器不会允许您(这是一件好事-在运行前捕获不匹配错误)。您也不能尝试使用不受支持的密钥


这种设计被称为。如果有什么你不同意的,或者需要进一步解释的,请告诉我

一般来说,当两个抽象之间的关系是一对多时,策略模式是好的。例如,如果您有一把锁和多把钥匙可以用来打开锁。例如,以下是战略模式的一个很好的例子:

public class Lock {

     public void unlock(Key key) {
         // Unlock lock if possible
     }
}

public interface Key {
    public int someState();
}

public class FooKey implements Key {

    @Override
    public int someState() { ... }
}

public class BarKey implements Key {

    @Override
    public int someState() { ... }
}
你的问题是一个多对多的问题,有许多锁可以通过多把钥匙打开,其中一些钥匙可以用来打开一些锁,而不能打开其他锁。对于这类问题,是一个很好的选择,其中算法是解锁过程,对象是锁。这种方法的好处是成功或失败锁(无论特定钥匙是否解锁特定锁)包含在简单方法中,而不使用
instanceof

通常,使用
instanceof
表示需要某种形式的多态性(即,不必测试每个提供的对象以确定其是否为某个类型并基于该类型执行逻辑,该类型应具有多态方法,其行为因对象类型而异)。这个问题非常常见,有一个标准的重构来替代它:

为了实现访问者模式,您可以尝试以下类似的方法:

public class UnlockFailedException extends Exception {

    public UnlockFailedException(Lock lock, Key key) {
        this("Key " + key.getClass().getSimpleName() + " failed to unlock lock " + lock.getClass().getSimpleName());
    }

    public UnlockFailedException(String message) {
        super(message);
    }
}

public interface  Lock {
    public void unlock(Key key);
}

public interface Key {
    public void unlock(FacialRecognizer lock) throws UnlockFailedException;
    public void unlock(FingerPrintRecognizer lock) throws UnlockFailedException;
}

public class FacialRecognizer implements Lock {

    @Override
    public void unlock(Key key) {
        key.unlock(this);
    }
}

public class FingerPrintRecognizer implements Lock {

    @Override
    public void unlock(Key key) {
        key.unlock(this);
    }
}

public class FacePhoto extends Key {

    @Override
    public void unlock(FacialRecognizer lock) throws UnlockFailedException {
        // Unlock the lock
    }

    @Override
    public void unlock(FingerPrintRecognizer lock) throws UnlockFailedException {
        throw new UnlockFailedException(lock, this);
    }
}

public class FingerPrint extends Key {

    @Override
    public void unlock(FacialRecognizer lock) throws UnlockFailedException {
        throw new UnlockFailedException(lock, this);
    }

    @Override
    public void unlock(FingerPrintRecognizer lock) throws UnlockFailedException {
        // Unlock the lock
    }
}
将每个
Lock
unlock
逻辑分组到一个抽象类中(因为每个
Lock
实现都是一样的),这可能会破坏模式。通过将
传递给提供的
,编译器知道要调用哪个重载方法。这个过程叫做。虽然可能看起来很乏味,但调用的逻辑很简单(一行),因此,尽管存在重复,但并不严重

这种方法的缺点是,
接口必须为每个
实现有一个
解锁
方法。如果缺少一个,编译器将在执行
Lock
实现时进行投诉,因为其
unlock
方法将调用
Key
上的
unlock
,该方法不包含接受新
Lock
实现的方法。从这个意义上说,编译器充当一种检查,确保
密钥
实现可以处理(解锁或未能解锁)每个
实现

您还可以实现一个
钥匙圈
,该钥匙圈包含许多
钥匙
对象。您可以使用每个
钥匙
对象解锁
,直到找到一个打开
锁的对象。如果可以打开
锁的
钥匙圈上没有
钥匙
,则会出现
解锁故障异常

public class KeyRing {

    public final List<Key> keys = new ArrayList<>();

    public void addKey(Key key) {
        keys.add(key);
    }

    public void removeKey(Key key) {
        keys.remove(key);
    }

    public void unlock(Lock lock) throws UnlockFailedException {

        for (Key key: keys) {
            boolean unlockSucceeded = unlockWithKey(lock, key);
            if (unlockSucceeded) return;
        }

        throw new UnlockFailedException("Could not open lock " + lock.getClass().getSimpleName() + " with key ring");
    }

    private boolean unlockWithKey(Lock lock, Key key) {
        try {
            lock.unlock(key);
            return true;
        }
        catch (UnlockFailedException e) {
            return false;
        }
    }
}
使用
布尔值
返回值还简化了
键环
的实现:

public class KeyRing {

    public final List<Key> keys = new ArrayList<>();

    public void addKey(Key key) {
        keys.add(key);
    }

    public void removeKey(Key key) {
        keys.remove(key);
    }

    public boolean unlock(Lock lock) throws UnlockFailedException {

        for (Key key: keys) {
            boolean unlockSucceeded = lock.unlock(key);
            if (unlockSucceeded) return true;
        }

        return false;
    }
}
公共类密钥环{
public final List key=new ArrayList();
公共无效addKey(Key-Key){
key.add(key);
public class KeyRing {

    public final List<Key> keys = new ArrayList<>();

    public void addKey(Key key) {
        keys.add(key);
    }

    public void removeKey(Key key) {
        keys.remove(key);
    }

    public boolean unlock(Lock lock) throws UnlockFailedException {

        for (Key key: keys) {
            boolean unlockSucceeded = lock.unlock(key);
            if (unlockSucceeded) return true;
        }

        return false;
    }
}