Скрытие классов в файле jar

Неужели невозможно скрыть некоторые классы в jar файле?

Я хотел не допускать прямого создания экземпляров классов, чтобы сделать его более гибким. Из этой банки должен быть виден только завод (или фасад).

Есть ли другой способ, кроме как решить эту проблему, кроме создания двух проектов? (Два проекта: первый содержит классы (реализация), а другой ссылается на первый и содержит фабрику; позже будет ссылка только на второй)


person nrainer    schedule 21.01.2011    source источник
comment
Я не совсем понимаю, что вы имеете в виду, говоря о сокрытии. Файлы Jar - это просто файлы .zip, поэтому любой может открыть их и увидеть ваши файлы классов внутри них.   -  person jonescb    schedule 21.01.2011
comment
Я просто хочу избежать того, чтобы классы можно было использовать извне (случайно) для создания экземпляров.   -  person nrainer    schedule 21.01.2011
comment
Я действительно думаю, что вы должны доверять пользователю ваших классов и предоставить достаточно приличную документацию о том, что использовать, а что нет. В любом случае вы не сможете предотвратить все ошибки, которые могут совершать разработчики, использующие ваши jar-файлы. Одна альтернатива, конечно же, - просто сделать свои собственные конструкторы настолько сложными, что никто не сможет их понять и естественным образом вернется к фабричным классам: D   -  person Mikko Wilkman    schedule 21.01.2011
comment
Также я не думаю, что есть смысл добавлять такую ​​гибкость, пока она действительно для чего-то не используется. И просто для гибкости, возможно, вы могли бы изучить некоторые фреймворки Inversion of Control, такие как Spring (или что-то более легкое, в зависимости от ваших потребностей).   -  person Mikko Wilkman    schedule 21.01.2011


Ответы (8)


Я думаю, у вас будет либо сбой компилятора, либо предупреждение, если ваш общедоступный фабричный метод попытается вернуть что-то «скрытое».

Нет, вы не можете скрыть публичный класс, не реализовав свой собственный ClassLoader, не используя OSGi или что-то подобное.

Что вы можете сделать, так это отделить интерфейс api от реализации, например иметь один проект, содержащий только интерфейсы, и другой проект, содержащий имплементации. Однако скрыть классы реализации по-прежнему нельзя.

person gigadot    schedule 21.01.2011
comment
Вот что я имел в виду, создав два проекта. Я думаю, что сделаю это, даже если это увеличит количество проектов. - person nrainer; 21.01.2011

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

person Mikko Wilkman    schedule 21.01.2011
comment
На самом деле, таким образом любой может вызывать ваши конструкторы, создавая свои собственные фабричные классы и помещая их в один и тот же пакет ... Может, лучше было бы начать с почему вы хотите скрыть эти вещи? - person Mikko Wilkman; 21.01.2011
comment
Ты получил это. Но проблема как раз в ограничении. Классов много, и я действительно не хочу объединять их в один пакет. - person nrainer; 21.01.2011
comment
@ Mikko: На ваш комментарий: Это можно решить, запечатав упаковку. - person nrainer; 21.01.2011

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

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

  1. Вы можете превратить все свои классы в частные внутренние классы фабрики. . Это сработало бы, если бы у вас была одна фабрика на класс, но вряд ли сработает, если у вас есть много разных классов, управляемых через одну фабрику.
  2. Вы можете использовать модификатор доступа protected, чтобы ограничить доступ к конструкторам вашего класса. Это обычная практика при использовании заводского шаблона.
person Mia Clarke    schedule 21.01.2011
comment
Первый, как вы сказали, глупый. У второго есть проблема в том, что фабрика должна быть в одном пакете. - person nrainer; 21.01.2011
comment
@nrainer :) Я использовал его в тех случаях, когда у вас есть фабрика только для одного класса с множеством вариантов создания экземпляров, но кроме этого конкретного случая, это не рекомендуется. - person Mia Clarke; 21.01.2011
comment
Я считаю, что этот ответ идеально подходит для моих требований, когда я хочу скрыть полную бизнес-логику моей библиотеки от приложения-потребителя. - person Gurunath Sripad; 06.05.2020

Обфускация может помочь вам как-то.

person jmj    schedule 21.01.2011
comment
Прикомандированный, хотя это и не скрывает. - person Ilya Saunkin; 21.01.2011
comment
Хорошо, спасибо за ответ. Тем не менее, я думаю, что это перебор для моих требований. - person nrainer; 21.01.2011

Со стандартными загрузчиками классов и простыми старыми файлами jar это невозможно. В OSGi есть концепция сделать видимыми только некоторые пакеты для другого пакета (т. Е. Разделение общедоступного API и внутренней реализации).

Если вы используете eclipse, вы можете применить такие правила с помощью this

person lweller    schedule 21.01.2011

Если я правильно вас понял, когда вы говорите «не разрешать прямое создание экземпляров классов, чтобы сделать его более гибким», правильно выполненный паттерн фасада справится с этим.

Ограничьте конструкторы всех классов, которые вы хотите скрыть, областью пакета. Откройте класс фасада в публичной области.

http://mindprod.com/jgloss/packagescope.html

«Если у вас есть переменная или метод в вашем классе, к которым вы не хотите, чтобы клиенты вашего класса имели прямой доступ, не давайте ему публичное, защищенное или частное объявление. Из-за недосмотра в дизайне Java вы можете» t явно объявить доступность «пакета» по умолчанию. Другие члены пакета смогут его видеть, но классы за пределами пакета, наследующие от вашего, не будут. Атрибут защищенного доступа обеспечивает немного более заметную видимость. Защищенный метод виден для наследования классов, даже если они не являются частью одного и того же пакета. Метод области действия пакета (по умолчанию) - нет. Это единственное различие между защищенной областью и областью пакета ».

person Speck    schedule 21.01.2011
comment
К сожалению, фасад должен быть в той же упаковке. Так как классов много, я этого не хочу. - person nrainer; 21.01.2011
comment
Я не экспериментировал, но вы должны иметь возможность поместить свой класс фасада в один пакет и иметь свои классы реализации в дочерних пакетах. Таким образом, фасадный и реализующий классы находятся в одном пакете, и вы получаете организацию, состоящую из множества пакетов. - person Speck; 21.01.2011
comment
Насколько я знаю, pck1.subPck1 не имеет ничего общего с pck1. Поэтому не думаю, что это сработает. (Пространства имен в C # позволили бы это. В любом случае, если бы я использовал C #, у меня не было бы этой проблемы из-за внутреннего модификатора, который решил бы мою проблему.) - person nrainer; 21.01.2011
comment
+1 кажется это самый простой способ. просто выполните 'String str;' вместо 'public / private / protected String str;' - person greenhouse; 20.02.2015

Есть два решения вашего вопроса, которые не связаны с хранением всех классов в одном пакете.

Первый - использовать шаблон Friend Accessor / Friend Package, описанный в (Практическое проектирование API , Тулач 2008).

Второй - использовать OSGi. здесь, объясняющая как OSGi это делает.

Связанные вопросы: 1, 2, 3 и 4.

person Jeff Axelrod    schedule 27.05.2011

Вы можете творить такую ​​магию с помощью специального загрузчика классов, но:

  • правильное разделение будет доступно только в проекте, укомплектованном вашим загрузчиком классов;
  • действительно сомнительно, что усилия по созданию такого загрузчика достойны.

В таких ситуациях я бы сделал что-то похожее на то, что мы можем видеть в стандартной Java. Например, вы видите javax.xml.stream.XMLInputFactory, но где-то у вас есть com.sun.xml.internal.stream.XMLInputFactoryImpl. Он отлично компилируется, если вы напишете:

new com.sun.xml.internal.stream.XMLInputFactoryImpl()

хотя вы вряд ли это сделаете :-) С помощью системного свойства вы можете контролировать фактическую загружаемую реализацию. Мне такой подход подходит во многих ситуациях.

Надеюсь, я правильно понял ваш вопрос;)

Ваше здоровье!

person Lachezar Balev    schedule 21.01.2011