【问题标题】:Get all types of a Decorator Object: type of wrapped objects and the type of wrapper object获取装饰器对象的所有类型:包装对象的类型和包装对象的类型
【发布时间】:2019-10-17 19:32:15
【问题描述】:

所以,这是我的设计。 AccessDecorator 类引用另一个 Access 就像普通的装饰器模式一样。

但问题是当我创建一个 AccessDecorator 包装一个 ConcreteAccess 然后尝试查看 Access 是哪种类型时:

Access access = new InAllowedAccess();
Access wrapperAccess = new MismatchAccess(access); 
if (wrapperAccess instanceof InAllowedAccess)   //this condition could be used to be a predicate for a filtering over an access list for example
    //do something

这当然行不通,因为wrapperAccess 不是InAllowedAccess 类型但我真正想知道的是一些Access 的所有类型。 在这种情况下, wrapperAccess 不仅是 MismatchAccess 类型,而且也是 InAllowedAccess 类型

我考虑在Access 类中实现isInstanceofInAllowed()isInstanceofOutAllowed()isInstanceofInDenied()isinstanceofOutDenied()isinstanceofMismatch() 等方法,但似乎不是一个好的解决方案,我不知道...

否则我应该为InAllowedMismatchAccessOutAllowedMismatchAccessInDeniedMismatchAccessOutDeniedMismatchAccess 的每 4 种类型创建一个带有MismatchAccesses 的大层次树吗?然后,当我开发另一个装饰器时?...

或者还有其他更好的设计吗?

我如何知道Access 的所有类型?不仅是封装访问的类型,还有封装访问的类型。

编辑: 我的一个需求是:按类型过滤Accesses 的集合——ÌnAllowedAccessInDeniedAccessOutAllowedAccessOutDeniedAccessMismatchAccess(这是一个装饰器)和我其他类型的装饰器可能会发展

【问题讨论】:

  • 装饰器模式依赖于多态性。测试对象的类型不是多态性。使用 Access 的代码应该使用它,而不必知道和关心对象的实际类型:它应该只调用接口中定义的方法,仅此而已。我们对这些类应该做什么知之甚少,因此很难比这更精确。
  • 这很难解释,但谢谢。所以创建像isInstanceofInAllowed(),...这样的方法也不好,对吧?
  • 不,不会的。

标签: java design-patterns instanceof decorator-pattern


【解决方案1】:

避免类型检查通常是最好的方法。不幸的是,您没有提供足够的上下文来说明您将如何使用您的类,因此我可以举例说明如何使用多态性并避免它。

添加类型检查将限制系统的增长能力,因为随着新类的添加,这些类型需要包含在类型检查中。有时这会导致错误,因为您的代码可以假设类的数量或其类型。这是一个例子:

注意:我只是为了说明目的而编造的。这不是必须代表你的逻辑或类似的东西。

public void someMethod(Access access) {

    if(access instance of InAccess) { 
        InAccess inAccess = (InAccess)access;
    }
    else {
        OutAccess outAccess = (OutAccess)access;
    }
}

当我们启动时,我们的系统有两个继承自 Access 的类。假设我们向我们的系统添加另一个Access class。这段代码会在 else 上崩溃,因为我们可能会传递新的第三个访问类型并且强制转换不会成功。

当然,情况并非总是如此。有时,您拥有的课程数量不会增长太多。有可能您可以预测所有类型。

当然,因为所有事情都可能发生在编程中,所以有时你确实需要知道你正在使用的对象的类型。

假设您的系统确实需要知道对象的类型。这里有两个解决方案:

  1. 添加一个表示您拥有的所有类型的枚举。

public enum AccessType { InAccessAllowed, InAccessDenied, OutAccessDenied, // other types }

public interface Access {
   AccessType getType();
   // other stuff
}

这样您将使用enum AccessType 而不是类型转换。

  1. 使用接口。

不使用类,而是为Access 的每种类型定义一个接口。然后您将检查接口而不是类。这样,您的装饰器可以实现与它所装饰的类相同的接口。

public interface InAllowedAccess { }

public class InAllowedAccessImp implements InAllowedAccess { }

public class InAllowedAccessDecorator implements InAllowedAccess { }

我只是不想给出替代​​实现的示例。由于您的描述中缺少上下文,我将尝试猜测您将如何使用您的类并向它们添加行为。这只是一个想法而已。

假设您的系统授予用户访问权限。可以为用户提供 In 和 Out 访问权限,并且系统的某些部分需要询问是否授予或拒绝特定用户的访问权限,以便它可以执行特定的逻辑。

如果您没有与您的Access classes 相关联的任何行为,您可以将其用作描述符,该描述符将携带其他类完成工作所需的信息。

public enum PortType { In, Out }
public enum Permissions { Allowed, Denied }

public class Access {
    private PortType mPortType;
    private Permissions mPermissions;

    public Access(PortType portType, Permissons permissions) {
        mPortType = portType;
        mPermissions = permissions;
    }

    public PortType getType() { return mPortType; }
    public Permissions getPermissions() { return mPermissions; }
}

如果你确实有行为,那么你可以使用多态性。在Access interface 中定义行为,并让实现此接口的类定义行为。

假设我们有一个用户可以接收(输入)和发送(输出)消息的消息传递系统。这些消息通过一个渠道。这些通道将接受或拒绝消息。这是一种使用多态性代替类型检查的方法。

public interface MessageChannel {

    public bool canSendMessages(); // out
    public bool canReceiveMessages(); // in

    public void receiveMessage(Message m);
    public void sendMessage(Message m);
}

public class InMessageChannel implements MessageChannel {

    // out messaging is not allowed, cannot send
    public bool canSendMessages() { return false; } 

    // In messaging allowed, can receive
    public bool canReceiveMessages() { return true; } 

    public void sendMessage(Message m) {
        throw new SendingMessagesIsNotAllowd();
    }
    public void receiveMessage(Message m); { 
        // do something with the mssage
    }
}

public class OutMessageChannel implements MessageChannel {

    // out messaging allowed
    public bool canSendMessages() { return true; } 

    // In messaging not allowed
    public bool canReceiveMessages() { return false; } 

    public void sendMessage(Message m) {
        // send the message
    }

    public void receiveMessage(Message m); { 
        throw new ReceivingMessagesIsNotAllowd();
    }
}

如您所见,每个MessageCahnnel 都有一个与之相关的行为。如果允许或不允许,它可以发送接收消息。这样其他使用 MessageChannel 的类就不必进行类型转换了。

【讨论】:

  • 感谢您的回答。我想我只需要在这种情况下检查类型:我有一个Accesses 的集合,我想按它们的类型过滤集合。我认为在这种情况下,我无法避免检查类型或创建一些枚举。但是你能解释一下创建枚举和实现方法 getType() 而不是方法isInstanceofInAllowed()isInstanceofOutAllowed() 等有什么不同吗?只是为了澄清。谢谢!
  • 不客气 :) 枚举和IsInstanceOfInAllowed() 方法之间的区别在于,当您添加另一种访问类型时,您需要添加另一种IsInstanceOf{Type} 方法,这将导致级联更改在您现有的所有课程中。如果枚举不会发生,您只需向枚举添加另一个值,系统中不会发生任何其他变化。新的decorators 和新的具体Access 类都只会返回新的枚举值。编写if else 语句和过滤时仍然需要小心,但如果你这样做了,你会没事的。
【解决方案2】:

我想过在 Access 类中实现 isInstanceofInAllowed()、isInstanceofOutAllowed()、isInstanceofInDenied() 和 isinstanceofOutDenied() 等方法,但似乎不是一个好的解决方案,我不知道...

你是对的。这是一个糟糕的解决方案。接口通常属于软件中抽象程度较高的层,因此其方法列表应该是稳定的。如果你在Access接口里面放了一堆上面这样的方法,这个接口会很不稳定,以后很可能你会往里面添加更多这样的方法。

对您的问题最简单的解决方案是(仅一次)向Access 接口添加一个名为core() 的新方法。每个装饰器只是通过返回包装/核心对象来实现此方法。

interface Access {
    ...
    Access core();
}

Access a = ...
if (a.core() instanceof ...

【讨论】:

  • 谢谢!这对我的问题来说是个好主意。但是我不仅需要检查核心类型,有时我还需要知道访问是否为MismatchAccess 类型(这是一个装饰器),例如,以同样的方式检查是否为DeniedAccess 类型,例如。
猜你喜欢
  • 2016-03-09
  • 2015-08-12
  • 2022-10-07
  • 1970-01-01
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多