简体   繁体   English

强制我的图书馆的用户实现接口或扩展抽象类

[英]Force a user of my library to implement an interface or extend an abstract class

I'm developing an android library (.aar) and I was wondering if it was possible to, as the title suggest, force a user to implement an interface or extend an abstract class of my library. 我正在开发一个Android库(.aar),我想知道是否有可能如标题所示强制用户实现接口或扩展我库的抽象类。

I already know that I could just go with a class like this in my library : 我已经知道我可以在我的库中使用这样的类:

public class MyLibrary
{
    public interface VariablesInterface
    {
        void createVariables();
    }

    private static VariablesInterface vi = null;

    public void setVariablesInterface(VariablesInterface v)
    {
        vi = v;
    }

    private static void SomeWork()
    {
        if (vi == null)
        {
            throw new RuntimeException("You noob.");
        }
        else
        {
            // do work
        }
    }
}

The library will work "alone" at some point, and when it will come to SomeWork() , if the interface isn't implemented it will crash, but this could only be seen at runtime. 该库将在某个时候“单独”工作,当涉及SomeWork() ,如果未实现该接口,它将崩溃,但这只能在运行时看到。

Is there a way to have this behaviour when compiling the user's application ? 编译用户的应用程序时,有没有一种方法可以解决此问题?

The goal is to avoid the user forgetting that he have to implement this without having to write it in the documentation and hope the user will probably read it. 目的是避免用户忘记自己必须实现而不必在文档中编写它,并希望用户可能会阅读它。

Thanks for reading ! 谢谢阅读 !


EDIT 编辑

I think that this question need some enhancement and background. 我认为这个问题需要一些增强和背景。 The purpose of the library is to provide classes that create variables which manages preferences, eg : 该库的目的是提供创建用于管理首选项的变量的类,例如:

public class VarPreferenceBoolean extends VarPreference
{
    private boolean defaultValue;

    public VarPreferenceBoolean(String key, boolean defaultValue)
    {
        super(key, true);
        this.defaultValue = defaultValue;
    }

    public void setValue(Context context, boolean value)
    {
        SharedPreferences.Editor e = context.getSharedPreferences(PropertiesManager.preferenceFileName, Context.MODE_PRIVATE).edit();
        e.putBoolean(key, value);
        e.commit();
    }

    public boolean getValue(Context context)
    {
        readPropFile(context);
        SharedPreferences sp = context.getSharedPreferences(PropertiesManager.preferenceFileName, Context.MODE_PRIVATE);
        return sp.getBoolean(key, defaultValue);
    }
}

The same goes for int, string and so on. int,string等也是如此。 In the super class, I add each VarPreference to a List to keep the library acknowledged of all the variables availables. 在超类中,我将每个VarPreference添加到一个List中,以使库确认所有可用变量。 Note the readPropFile inside the getter. 注意getter内部的readPropFile

Then, the user use the library in his project like this : 然后,用户使用他的项目中的库,如下所示:

public class Constants
{
    public static final VarPreferenceInt     FILETYPE;
    public static final VarPreferenceInt     DATAMODE;
    public static final VarPreferenceString  URL_ONLINE;
    public static final VarPreferenceBoolean UPDATING;
    public static final VarPreferenceLong    LAST_UPDATE;

    static
    {
        FILETYPE = new VarPreferenceInt("FileType", MyFile.FileType.LOCAL.getValue());
        DATAMODE = new VarPreferenceInt("DataMode", DataProvider.DataMode.OFFLINE.getValue());
        URL_ONLINE = new VarPreferenceString("UrlOnline", "http://pouetpouet.fr");
        UPDATING = new VarPreferenceBoolean("Updating", false);
        LAST_UPDATE = new VarPreferenceLong("LastUpdate", 0L);
    }
}

Now, when the user call an accessor, readPropFile will first search if a .properties file exist and modify accordingly the preferences if it found matches between the list of VarPreference and the properties of the file. 现在,当用户调用访问器时, readPropFile将首先搜索.properties文件是否存在,并在发现VarPreference列表和文件属性之间匹配时,相应地修改首选项。 Then it will delete the file and the accessor will return the value. 然后它将删除文件,访问器将返回该值。

This is what exists today. 今天就是这样。

Now we want another application (let's say Pilot) to be able to get the VarPreferences of the user's application (let's say Client). 现在,我们希望另一个应用程序(例如Pilot)能够获取用户应用程序(例如Client)的VarPreferences。 Both implements the library. 两者都实现该库。

Pilot send an Intent asking for the VarPreference list of Client, putting in extra the package name of Client. 飞行员发送一个Intent来请求Client的VarPreference列表,并额外添加Client的程序包名称。 The library receive the intent, verify the packagename, if it's Client it send back the list. 库接收到该意图,请验证软件包名称,如果是客户端,则将其发回列表。

Problem is, if Client hasn't started, no VarPreference exists, and the list is empty. 问题是,如果Client尚未启动,则不存在VarPreference,并且列表为空。

I need to force the user to create his VarPreference in an method that my library know, to be able to call it whenever I want, and create the VarPreferences of the user when it's necessary. 我需要强迫用户以我的库知道的方法创建他的VarPreference,以便能够在需要时调用它,并在必要时创建用户的VarPreferences。

Hope this is clearer ! 希望这更清楚!


EDIT 编辑

I rethought about all of this with a colleague and it just hit us that all this stack is biaised. 我和一位同事一起考虑了所有这一切,这让我们感到震惊的是,所有这一切都是偏颇的。

I didn't explain well and even if I said it, I didn't take account enough of this : everything needs to be done from the library. 我没有很好地解释,即使我说了也没有足够考虑:一切都需要从库中完成。 So, even if I give an interface to the library, the application will have to run and call this affectation first in order to let the library work alone. 因此,即使我为该库提供了接口,该应用程序也必须先运行并调用此操作才能使该库单独工作。

We are heading towards introspection now. 我们现在正要进行内省。 (This is the goal, it may not be possible...) There will be an abstract class inside the library, with an abstract method where the user will place all of the VarPreferences creations. (这是目标,可能无法实现...)库中将有一个抽象类,其中包含一个抽象方法,用户可以在其中放置所有VarPreferences创建内容。 The user will have to extends this class and call the method in order to create his VarPreferences. 用户将必须扩展此类并调用该方法才能创建他的VarPreferences。 In the library, a method will search by introspection a child of the abstract class, create an instance of this child and call the method that will create the VarPreferences. 在该库中,一种方法将通过内省搜索抽象类的子级,创建该子级的实例,然后调用将创建VarPreferences的方法。

I would leave the abstract classes and interfaces in the main library and load the rest of your code via classloader from another. 我将把抽象类和接口留在主库中,并通过类加载器从另一个库中加载其余代码。 JDBC works like this. JDBC的工作原理是这样的。

Is there a way to have this behaviour when compiling the user's application ? 编译用户的应用程序时,有没有一种方法可以解决此问题?

I see no way to force a compilation failure. 我认为没有办法强制编译失败。 However, if you force them to supply a VariablesInterface in the constructor then it will fail immediately. 但是,如果强迫他们在构造函数中提供VariablesInterface ,则它将立即失败。 Make the VariablesInterface be final and only initialize it in the constructor: 使VariablesInterfacefinal并仅在构造函数中对其进行初始化:

 public class MyLibrary {
     private final VariablesInterface vi;

     public MyLibrary(VariablesInterface vi) {
        if (vi == null) {
            throw new IllegalArgumentException("vi can't be null");
        }
        this.vi = vi;
     }
     ...

If you can't change the constructor then you can also add to any SomeWork public methods some sort of configuration check method to make sure the the vi wiring has properly been done but this requires careful programming to make sure all public methods are covered. 如果无法更改构造函数,则还可以向某种SomeWork公共方法添加某种配置检查方法,以确保正确完成vi接线,但这需要仔细编程以确保覆盖所有公共方法。

public void somePublicMethod() {
    checkWiring();
    ...
}

private void checkWiring() {
    if (vi == null) {
        throw new IllegalStateException("vi needs to be specified");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM