Сокрытие информации в Groovy (с использованием замыканий "соглашения об именах")
Это следует из моей неудачной попытки найти ответ на этот вопрос с 2014 года.
Мне не ясно, могут ли на самом деле в Groovy быть какие-то методы использования замыканий, в частности, для сокрытия информации. Все, что я могу сказать, это то, что если информация о таких методах существует, то это точная иллюстрация "сокрытия информации": я не могу ее найти!
Но если мне не удастся понять, что я сейчас понимаю, так это то, что когда-либо предпринималась абсолютно нулевая попытка скрыть информацию (или сделать вид, как в Java - с учетом методов отражения). Похоже, что это из-за дизайна, но также из-за требований динамического характера Groovy. Кажется, например, что @CompileStatic
Упоминается в упомянутом вопросе, больше о проверке типов, чем что-либо еще.
Но в Python, например, существует соглашение (я полагаю, все еще используется), чтобы "поля, которые должны рассматриваться как частные", начинались с двойного подчеркивания. Я никогда не слышал, чтобы кто-то говорил об этом в связи с Groovy.
Разве это не сокрытие информации и не инкапсуляция или, по крайней мере, соглашения, поощряющие дисциплинированное использование "интимного состояния" объектов, хорошие вещи? Любые специалисты Groovy хотят прокомментировать?
потом
Даггетт дал ответ, который интересен в некоторых отношениях, но не совсем то, что я имел в виду. Учти это:
class Main {
static main( args ) {
def sm = new SecurityManager()
System.setSecurityManager( sm )
println new Bob().doSomethingProtected()
}
}
class Bob {
public doSomethingPublic() {
"public"
}
private doSomethingPrivate() {
"private"
}
protected doSomethingProtected() {
"protected"
}
}
... какой бы из них Bob
методы называется это пройдет с SecurityManager
не установлен, но не работает с ним. Также не имеет значения, в какой упаковке он находится. Не имеет значения, Bob
находится в подпакете (например), с @PackageScope
: это только если Main.main
дано @CompileStatic
что это поможет (см. ссылочный вопрос). Мне также не ясно, что именно вы можете сделать с SecurityManager
установить таким образом: возможно ли обеспечить соблюдение private
или же protected
(или пакет-приват) каким-то образом? На данный момент я просто не знаю, и мне придется расследовать.
Что касается другого предложения, оно интригует, но на самом деле не отрицает "видимость", как было предложено. Вам также необходимо включить следующий метод в классе A
:
def getI() {
throw new Exception()
}
После этого, да, видимость запрещена для любого другого класса, будь то в том же пакете или нет, а также эти "частные" элементы даже не видны другим объектам того же класса (! - в отличие от Java). В этом смысле это действительно обеспечивает очень драконовскую конфиденциальность. Но для меня это тоже немного взломать. Я не совсем ясно об этом GroovyObjectSupport
класс или что он делает, и должен будет исследовать это. Наконец, нет смысла давать этим полям private
модификатор. Как я уже сказал, ЕДИНСТВЕННАЯ функция private
в Groovy - отрицать видимость этих полей для подклассов класса Даггетта A
Вот.
Имея только абсолютный выбор между супер-драконовским и хакерским "частным" или "неограниченно публичным", явно представляет собой значительное "обнищание" выбора видимости по сравнению с Java, где у вас есть не только protected
но также пакетно-приватный (тема, да, да, да, конечно, использование отражения...), и где private
поля видны другим объектам того же класса.
2 ответа
Менеджер по безопасности
не запускайте следующий код из
GroovyConsole
, только из заводной командной строки.
def sm = new SecurityManager()
System.setSecurityManager(sm)
//without previous lines the following code will run successfully
println new ByteArrayOutputStream().buf
это бросит следующее исключение
Caught: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:109)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:131)
Caused by: java.lang.ExceptionInInitializerError
at groovy.ui.GroovyMain.run(GroovyMain.java:397)
at groovy.ui.GroovyMain.process(GroovyMain.java:370)
at groovy.ui.GroovyMain.processArgs(GroovyMain.java:129)
at groovy.ui.GroovyMain.main(GroovyMain.java:109)
... 6 more
Caused by: java.security.AccessControlException: access denied ("java.util.logging.LoggingPermission" "control")
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
at java.util.logging.LogManager.checkPermission(LogManager.java:1586)
at java.util.logging.Logger.checkPermission(Logger.java:422)
at java.util.logging.Logger.setUseParentHandlers(Logger.java:1799)
at org.codehaus.groovy.runtime.StackTraceUtils.<clinit>(StackTraceUtils.java:57)
... 10 more
контролировать доступ с getProperty
& setProperty
class A extends GroovyObjectSupport{
private int i=555
private int j=666
def f(){
println "i=$i j=$j"
}
Object getProperty(String name){
if(name in ['i'])throw new Exception("Access to property `$name` is denied")
return super.getProperty(name)
}
}
def a=new A()
a.f()
println "a.j = ${a.j}"
println "a.i = ${a.i}"
это позволит получить доступ к члену j
но не члену i
вне класса.
выход:
i=555 j=666
a.j = 666
Exception thrown
java.lang.Exception: Access to property `i` is denied
...
Я создал расширение компилятора, которое позволяет выборочно или глобально применять проверки времени компиляции на предмет нарушения инкапсуляции. Кроме того, вы можете применить строгую проверку типов и проверку неизменности во время компиляции.https://github.com/stansonhealth/ast-framework
Пример:
package com.stansonhealth.ast.encapsulate
import com.stansonhealth.ast.encapsulate.Encapsulate
@Encapsulate
class TestEncapsulationFixture {
private int i
}
class TestEncapsulation {
void foo(TestEncapsulationFixture fixture) {
fixture.i++
fixture[i]++
fixture.i = 0
def x = fixture.i
}
}
Вывод компилятора:
startup failed:
TestEncapsulation: 14: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
@ line 14, column 21.
fixture.i++
^
TestEncapsulation: 15: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
@ line 15, column 21.
fixture[i]++
^
TestEncapsulation: 16: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
@ line 16, column 21.
fixture.i = 0
^
TestEncapsulation: 17: Field or property "i" not accessible for class = com.stansonhealth.ast.encapsulate.TestEncapsulationFixture
@ line 17, column 29.
def x = fixture.i
^
4 errors