【问题标题】:What is the best way to share only the getter methods to client?仅向客户端共享 getter 方法的最佳方法是什么?
【发布时间】:2015-11-27 08:40:36
【问题描述】:

我正在编写一个带有这样一个类的 API:

public class NKMPMission {
    private String name;
    private int age;
    public NKMPMission(String name, int age)
    {
         this.name  = name;
         this.age   = age;
    }
    public String getName() 
    {
       return name;
    }

    public int getAge() 
    {
       return age;
     }
   }

我的问题:

  1. 如何确保这个NKMPMission 类的用户只能访问getter?
  2. 如何为这个功能引入setter,让我作为开发者可以设置,但用户不能设置?

【问题讨论】:

  • 实际上,用户永远不会访问 getter 或任何方法,开发人员会......但用户只会使用应用程序
  • 我投票结束这个问题,因为它没有任何意义......
  • 我想将 NKMPMission 对象的数组列表返回给用户。
  • 很好,但是用户不会访问任何方法,或者您正在开发 API,当您说 user 时,您的意思是 开发人员使用您的 API?
  • 没错,Jordi,我正在开发一个 API,比如 getMissionList(),然后我返回一个 NKMPMission 对象列表。

标签: java


【解决方案1】:

通常的做法是根本不公开该类,只公开该类的接口。一个面向公众的界面,一个面向开发者的界面。

然后你需要一个工厂来创建它们。

/**
 * Expose this interface to the public.
 */
public interface INKMPMission {

    public String getName();

    public int getAge();

}

/**
 * Only expose this interface to developers.
 */
interface IDeveloperNKMPMission {

    public void setName(String name);

    public void setAge(int age);

}

public static class NKMPMissionFactory {

    /**
     * Expose only the INKMPMission construction.
     */
    public INKMPMission make(String name, int age) {
        return new NKMPMission(name, age);
    }

    /**
     * Protected version for developers.
     */
    IDeveloperNKMPMission forDeveloper(INKMPMission it) {
        return IDeveloperNKMPMission.class.cast(it);
    }

    /**
     * Private so no-one outside the factory knows about the inner workings.
     */
    private static class NKMPMission implements INKMPMission, IDeveloperNKMPMission {

        private String name;
        private int age;

        private NKMPMission(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public int getAge() {
            return age;
        }

        @Override
        public void setName(String name) {
            this.name = name;
        }

        @Override
        public void setAge(int age) {
            this.age = age;
        }
    }
}

对于真正偏执的人,您甚至可以使用代理。这将使通过反射使用设置器变得困难(但并非不可能)。

    /**
     * Expose only the INKMPMission construction.
     */
    public INKMPMission make(String name, int age) {
        return new NKMPMissionProxy(new NKMPMission(name, age));
    }

    /**
     * Protected version for developers.
     */
    protected IDeveloperNKMPMission forDeveloper(INKMPMission it) {
        if (it instanceof NKMPMissionProxy) {
            it = ((NKMPMissionProxy) it).theMission;
        }
        return IDeveloperNKMPMission.class.cast(it);
    }

    /**
     * A proxy for the truly paranoid - makes using reflection more difficult (but not impossible)
     */
    private static class NKMPMissionProxy implements INKMPMission {
        private final NKMPMission theMission;

        private NKMPMissionProxy(NKMPMission theMission) {
            this.theMission = theMission;
        }

        @Override
        public String getName() {
            return theMission.getName();
        }

        @Override
        public int getAge() {
            return theMission.getAge();
        }


    }

【讨论】:

    【解决方案2】:

    1) 我如何确保这个 NKMPMIssion 类的用户只能访问 getter。

    你不能。

    2) 我怎样才能为这个功能引入setter,这样作为开发者我应该可以设置,但用户不应该可以设置。

    听起来您正在编写 API。如果您从该 API 的公共方法返回 NKMPMIssion 实例,则可以调用 setter。即使您将它们标记为privateprotected,它们仍然可以通过反射调用。也就是说,通常将它们设为非public 就足够了。它至少会说“如果你调用这些,你就处于不受支持的领域。”

    如果你想让它变得更难,你可以返回一个在NKMPMIssion 实例周围包裹 facade 的实例。但这只会让事情变得更难,并非不可能,因为外观实例必须具有对 NKMPMIssion 实例的引用,该实例(即使是 private)可以通过反射访问。

    【讨论】:

      【解决方案3】:

      最简单的方法是让 API 只使用接口,并使类成为实现细节。

      public interface INKMPMission {        
          String getName();
          int getAge();
       }
      
      public class SomeService{
          private class MyNKMPMission implements INKMPMission {
              //put getters and setters here
          }
          public List<INKMPMission> getMissions(){
              //put some MyNKMPMissions in a list
          }
      }
      

      由于 MyNKMPMission 是私有的,因此消费者将永远无法进行向下转换并访问设置器。

      【讨论】:

        【解决方案4】:

        可以(以某种方式),但你不应该那样做:

        • 在每个 setter 中构造一个新的异常
        • 检查生成的 Stacktrace
        • 如果调用者类不在您的包中(或硬编码一些直接的类名/方法名),则抛出 IllegalAccessError

        这种方式既不美观也不快速,因为您必须检查对 setter 的每一次访问。

        另一种方法是使用 @CallerSensitive 注释,尽管它是专有 API,因此不适用于所有平台/jre 实现:https://stackoverflow.com/a/22627383/1164913

        干净且在大多数情况下足够的方法是使用仅向客户端提供 getter 的接口,然后将其返回给客户端。

        【讨论】:

          【解决方案5】:

          您似乎正在尝试编写 API。假设用户是指使用您的 API 的开发人员。

          在这种情况下,将 setter 设置为 protected 并构建一些有意义的包结构,这样只有孩子和包成员才能看到。

          如果你想保护它,即使是孩子的,除了将其设为私有之外别无他法。

          【讨论】:

          • 反对者,想发表评论吗?如果答案错误,请纠正我们。最好的办法是,添加您的答案,以便我们从中学习。
          【解决方案6】:

          回答问题:

          第一季度。你不能像T.J. Crowder在他的回答中所说的那样。

          第二季度。我建议您按照从最简单到最难的顺序尝试以下操作,并选择您认为最适合您的努力回报的选项:

          1. 看看:Java access modifiers
          2. 创建一个接口并将该接口与公共方法公开给“NKMPMission”类的“最终”用户,如Esben Skov Pedersen answer 中所述
          3. 终于可以做OldCurmudgeon answer中提到的代理方式了

          我会做什么:

          如果要在内部使用您的一组类,那么我会选择选项 1,结合良好的 javadoc 和项目标准,应该就足够了。

          如果您要创建公共 API,我会选择选项 2。

          恕我直言,在这种情况下,选项 3 增加了太多不必要的复杂性,而且几乎没有什么好处,因为无论如何都可以访问每个类方法或属性,从而引发反射(正如许多人所提到的)。我想每个人都知道这样一个事实,即对 API 隐藏方法的访问抛出反射是 hacky、危险且不方便项目的维护,因为 API 提供者有权更改隐藏方法的实现,而无需进一步通知最终用户.

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2016-12-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-07
            • 2010-09-18
            • 1970-01-01
            相关资源
            最近更新 更多