Как запретить клиенту видеть внутренние частные классы в библиотеке Android?


У меня есть библиотека с несколькими пакетами -

Скажем
пакет;
пакет b;

Внутри пакета a у меня есть public a_class
внутри пакета b у меня есть public b_class
a_class использует b_class.

Мне нужно создать библиотеку из этого, но я не хочу, чтобы клиент видел b_class.

Единственное решение, которое я знаю, - это свести мои прекрасно понятные пакеты к одному пакету и использовать доступ к пакету по умолчанию для b_class. Есть ли другой способ сделать это ? может быть, с помощью интерфейсов или какой-то формы Шаблона дизайна ??

4 22

4 ответа:

Мне нужно создать библиотеку из этого, но я не хочу, чтобы клиент видел b_class. Единственное решение, которое я знаю, - это сгладить мои красиво понятные пакеты до одного пакета и использовать доступ к пакету по умолчанию для b_class. Есть ли другой способ сделать это ?

Да, сделайте b_class пакет-частным (доступ по умолчанию) и создайте его экземпляр через отражение для использования в a_class.

Поскольку вы знаете полное имя класса, рефлекторно загрузите класс:

Class<?> clz = Class.forName("b.b_class")

Найдите конструктор, который вы хотите вызвать:

Constructor<?> con = clz.getDeclaredConstructor();

Позвольте себе вызвать конструктор, сделав его доступным:

con.setAccessible(true);

Вызовите конструктор, чтобы получить ваш экземпляр b_class:

Object o = con.newInstance();

Ура, теперь у вас есть экземпляр b_class. Однако вы не можете вызвать методы b_class на экземпляре Object, поэтому у вас есть два варианта:

  1. используйте отражение для вызова методов b_class (не очень весело, но достаточно легко и может быть хорошо, если вы только есть несколько методов с несколькими параметрами).
  2. должны b_class реализовать интерфейс, который вы не возражаете, чтобы клиент увидел и привел ваш экземпляр b_class к этому интерфейсу (читая между строк, я подозреваю, что у вас уже есть такой интерфейс?).

Вы определенно захотите пойти с вариантом 2, чтобы свести к минимуму вашу боль, если она не вернет вас снова к квадратному (загрязняя пространство имен типами, которые вы не хотите выставлять клиенту).

Для полного раскрытия, два Примечания:

1) есть (небольшие) накладные расходы на использование отражения против прямого создания экземпляров и вызова. Если вы приведете к интерфейсу, вы заплатите только стоимость отражения на экземпляре. В любом случае, это, скорее всего, не проблема, если вы не сделаете сотни тысяч вызовов в узком цикле.

2) нет ничего, что помешало бы решительному клиенту узнать имя класса и сделать то же самое, но если я правильно понимаю вашу мотивацию, вы просто хотите выставить чистый API, так что на самом деле это не проблема.

Если вы отказываетесь переместить код на отдельный управляемый сервер, все, что вы можете сделать, это помешать программисту клиента при попытке использовать ваши API. Давайте начнем применять хорошие практики к вашему дизайну:

  1. пусть ваши пакеты организованы так, как они есть сейчас.
  2. Для каждого класса, который вы хотите "спрятать":

    • сделать ее непубличной.
    • извлеките его открытый API в новый, открытый интерфейс:

    public interface MyInterface {...}

    • создание публичного factory class, чтобы получить объект этого типа интерфейса.

    public class MyFactory { public MyInterface createObject(); }

До сих пор ваши пакеты слабо связаны, а классы реализации теперь являются частными (как проповедуют хорошие практики, и вы уже сказали). Тем не менее, они все еще доступны через интерфейсы и фабрики.

Итак, как вы можете избежать того, что" чужие " клиенты выполняют ваши частные API? То, что происходит дальше, - это творческое, немного сложное, но верное решение, основанное на препятствующие клиентские программисты:

Измените классы фабрики: добавьте к каждому методу фабрики новый параметр:

public class MyFactory
{
    public MyInterface createObject(Macguffin parameter);
}
Итак, что же такое Macguffin? Это новый интерфейс, который вы должны определить в своем приложении, по крайней мере, с одним методом:
public interface Macguffin
{
    public String dummyMethod();
}

Но не предоставляют никакой полезной реализации этого интерфейса. В каждом месте вашего кода вам нужно предоставить объект Macguffin, создать его через анонимный класс:

MyFactory.getObject(new Macguffin(){
    public String dummyMethod(){
        return "x";
    }
});

Или даже больше продвинутый, через динамический прокси-объект , так что нет ".файл класса " этой реализации будет найден даже в том случае, если клиент-программист осмелится декомпилировать код.

Что вы получаете от этого? В основном это , чтобы отговорить программиста от использования фабрики, которая требует неизвестного, недокументированного, непонятного объекта. Фабричные классы должны просто позаботиться о том, чтобы не получить нулевой объект, и вызвать фиктивный метод и проверить возвращаемое значение, что оно также не является нулевым (или, если вы хотите повысить уровень безопасности, добавьте недокументированное правило секретного ключа).

Таким образом, это решение опирается на тонкое запутывание вашего API, чтобы отбить у программиста-клиента желание использовать его напрямую. Чем более неясными будут названия интерфейса Macguffin и его методов, тем лучше.

Если я правильно понял, вы спрашиваете о публикации вашей библиотеки для использования третьей стороной, не раскрывая часть вашего источника? Если это так, вы можете использоватьproguard , который может запутать вашу библиотеку. По умолчанию все будет исключено/запутано, если вы не укажете вещи, которые вы хотите исключить из запутывания / исключения.

Если вы хотите распространять [часть] вашего кода без того, чтобы клиент вообще имел к нему доступ, это означает, что клиент также не сможет его выполнить. :- O

Таким образом, у вас есть только один вариант: поместите разумную часть вашего кода на публичный сервер и распределите прокси для доступа к нему, так что ваш код будет сохранен и выполнен на вашем сервере, и клиент все еще сможет выполнить его через прокси, но без прямого доступа к нему.

Ты может использоваться сервлет, веб-сервис, объект RMI или простой TCP-сервер, в зависимости от уровня сложности кода.

Это самый безопасный подход, который я могу придумать, но он также заслуживает платы: в дополнение к усложнению вашей системы, он вводит сетевую задержку для каждой удаленной операции, которая может быть большой в зависимости от требований к производительности. Кроме того, вы должны обеспечить безопасность самого сервера, чтобы избежать хакерских атак. Это может быть хорошим решением, если у вас уже есть сервер, которым вы можете воспользоваться.