仅将getter方法共享给客户端的最佳方法是什么?

时间:2015-11-27 08:40:37

标签: java

我正在编写一个类似这样的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,以便我作为开发人员可以设置,但用户无法设置?

6 个答案:

答案 0 :(得分:21)

通常的做法是不公开类,只是将接口暴露给该类。一个公共界面和一个开发者界面。

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

/**
 * 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;
        }
    }
}

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

    /**
     * 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();
        }


    }

答案 1 :(得分:6)

  

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

你不能。

  

2)我如何为此功能引入setter,以便作为开发人员我应该能够设置,但用户不应该设置。

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

如果你想让它变得更难,你可以在NKMPMIssion实例周围返回一个包装 facade 的实例。但这只会让它变得更难,而不是不可能,因为Facade实例必须引用NKMPMIssion实例,这可以通过反射访问(即使它是private)。

答案 2 :(得分:4)

最简单的方法是让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是私有的,因此消费者永远无法进行向下转换并访问设置者。

答案 3 :(得分:3)

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

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

这种方式既不漂亮也不快,因为你必须检查对setter的每次访问。

另一种方法是使用@CallerSensitive Annotation,虽然它是propritary API,因此不适用于所有平台/ jre实现:https://stackoverflow.com/a/22627383/1164913

干净,在大多数情况下是足够的方式是使用一个接口,它只向客户端提供getter,并将其返回给客户端。

答案 4 :(得分:2)

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

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

如果你想保护孩子的这一点,除了把它变成私人之外别无选择。

答案 5 :(得分:2)

回答问题:

Q1。你不能像T.J. Crowder在他的回答中所说的那样。

Q2。我建议您按照以下顺序尝试以下事项,从最简单到最难,并采取您认为最适合努力回报的选项:

  1. 请查看:Java access modifiers
  2. 创建一个界面,并使用公共方法将该界面公开给&#34; final&#34; &#34; NKMPMission&#34;的用户class,如Esben Skov Pedersen answer
  3. 中所述
  4. 最后,您可以执行OldCurmudgeon answer
  5. 中提到的代理方法

    我会做什么:

    如果您的内部课程将在内部使用,那么我会将选项1与良好的javadoc和项目标准相结合,这应该足够了。

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

    恕我直言,选项3在这种情况下增加了太多不必要的复杂性,并且很少受益,因为无论如何都可以访问每个类方法或属性抛出反射(正如许多人所提到的)。我想每个人都知道这样一个事实,即API隐藏方法的访问权限反映是hacky,危险且不便于项目的管理,因为API提供商有权在没有进一步通知的情况下更改隐藏的方法实现给最终用户。