Java: существует ли путь к Вам, осуществляют реализацию закрытых методов?

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

Там какой-либо путь состоит в том, чтобы достигнуть этого? Я знаю, что интерфейсы работали бы отлично, но они не возьмут членов парламента, не занимающих официального поста и не позволят Вам переопределять объем в реализованном методе. Есть ли какое-либо обходное решение для этого?

Спасибо

6
задан John Baker 31 December 2009 в 17:12
поделиться

14 ответов

думаю, что ближе всего к этому можно приблизиться, используя абстрактный класс с абстрактным protected методами:

abstract class A {
    protected abstract void foo();
}

class B extends A {
    protected void foo() {}
}

Для определения общей логики можно вызвать защищенный метод из private метода в супер-классе:

abstract class A {
    private void bar() {
        // do common stuff
        foo();
    }
    protected abstract void foo();
}

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

.
16
ответ дан 8 December 2019 в 02:21
поделиться

Можно ли сделать так, чтобы все классы унаследовали от одного базового класса?

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

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

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

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

На самом деле я не имею опыта работы на Java, то, что я описываю, я реализовал в .NET, поэтому я не могу предоставить вам пример кода, но все концепции должны быть легко переносимыми на Java - все, о чем я упоминаю, доступно (AFAIK) на обеих платформах.

.
1
ответ дан 8 December 2019 в 02:21
поделиться

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

http://www.xdepend.com

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

Кроме того, она включает встроенный sql подобный языку запросов "CQL" (для "языка запросов кода"). Используя CQL, можно создавать собственные отчеты. Скорее всего, вы сможете использовать его для определения отчета о нарушении описанных вами правил. Также вы можете встраивать CQL-запросы непосредственно в свой код, используя аннотации.

Я не рассматривал его, но использовал его .NET-эквивалент 'NDepend', и это очень крутой инструмент.

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

.
1
ответ дан 8 December 2019 в 02:21
поделиться

Рассмотрим использование абстрактного базового класса. Вот старая, но актуальная статья: http://www.javaworld.com/javaworld/javaqa/2001-04/03-qa-0420-abstract.html

0
ответ дан 8 December 2019 в 02:21
поделиться

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

private final void doTransaction() {
    float amountDue;

    // a protected or abstract method that extenders can override
    Collection items = this.unloadShoppingCart();

    for (Object item : items) {
        // another protected or abstract method
        amountDue +=  this.getPrice(item);
    }

    // your private method 
    amountDue += this.getSalesTax(amountDue);

}
1
ответ дан 8 December 2019 в 02:21
поделиться

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

.
6
ответ дан 8 December 2019 в 02:21
поделиться

Во время компиляции не существует способа навязать его, но вы можете написать юнит-тест или простую программу для проверки существования методов с помощью рефлексии.

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

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

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Main
{
    public static void main(String[] args) 
    {
        check(B.class, Modifier.PRIVATE, void.class, "doit", new Class<?>[] { int.class });
        check(C.class, Modifier.PRIVATE, void.class, "doit", new Class<?>[] { int.class });
    }

    private static void check(final Class<?>   clazz,
                              final int        modifiers,
                              final Class<?>   returnType,
                              final String     name,
                              final Class<?>[] params)
    {
        try
        {
            final Method method;

            method = clazz.getDeclaredMethod(name, params);

            if(method.getModifiers() != modifiers)
            {
                System.out.println("modifiers do not match");
            }

            if(method.getReturnType() != returnType)
            {
                System.out.println("return type does not match");
            }
        }
        catch(final NoSuchMethodException ex)
        {
            System.out.println("could not find method");
        }
    }
}

interface A
{
    void foo();
}


class B
    implements A
{
    public void foo()
    {
        doit(0);
    }

    private void doit(final int x)
    {
    }
}

class C
    implements A
{
    public void foo()
    {
        doit(0);
    }

    private int doit(final int x)
    {
        return (5);
    }
}
3
ответ дан 8 December 2019 в 02:21
поделиться

Хм, частные функции не могут быть вызваны никакими другими классами, даже подклассами . Так какой смысл иметь приватные функции с одним и тем же именем в разных классах?

5
ответ дан 8 December 2019 в 02:21
поделиться

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

package com.some.car.pkg;

interface Car
{
    public void gas();
    public void brake();
}

Даже если методы публичны, это не имеет значения, так как вне пакета com.some.car.pkg, автомобиль не виден. Таким образом, все ваши реализации не будут вынуждены расширять абстрактный класс. Тот факт, что вам нужны общие методы, означает, что по-настоящему приватный - это не реальное решение, а IMHO, вам нужен интерфейс, так как в вашем случае абстрактный класс не совсем прав, так как нет общей логики.

Мои 2 цента.

2
ответ дан 8 December 2019 в 02:21
поделиться

Полезной конструкцией может быть "throw MethodNotImplementedException();"

.
1
ответ дан 8 December 2019 в 02:21
поделиться

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

  • производные классы должны определить поведение
  • производные классы не могут получить доступ к поведению после его определения
  • экземпляры не могут получить доступ к поведению друг друга

Например, с извинениями за за заимствование метафоры автомобиля, несмотря на отсутствие автомобильных отбивных:

public interface GearBoxStrategy {
    public void changeGear(int newGear);
}

abstract public class Car {
    private GearBoxStrategy gearBox;
    public Car(GearBoxStrategy g) {
       this.gearBox = g;
    }

    public void accelerate(double targetSpeed) {
        int gear = getTargetGear(targetSpeed):
        gearBox.shift(gear);
    }
}

public class AutomaticTransmissionCar {
    public AutomaticTransmissionCar() {
        super(new AutomaticTransmissionGearBoxStrategy());
    }
}

public class ManualTransmissionCar {
    public ManualTransmissionCar() {
        super(new ManualTransmissionGearBoxStrategy());
    }
}
1
ответ дан 8 December 2019 в 02:21
поделиться

Создайте "общий" класс, на котором будут находиться все ваши частные методы.

Затем создайте 5 или 6 классов, каждый из которых имеет поле типа "общий". Конечно, вы не сможете вызывать приватные методы (но вы говорите, что они действительно являются внутренними для класса) - вам, конечно, придется рекламировать некоторые публичные методы, чтобы изменить состояние.

public class common { 
    private method1() { ; }
    private method2() { ; }
    public other() { ; }
...
}

public class myclass1 { 
    common commonMethods;
}

public class myclass2 { 
    common commonMethods;
}

или даже (предположим, что "common" определено как выше):

public class template {
    common commonMethods;
}

public class myclass1 extends template {
...
}

Таким образом, вы получите (защищенное от пакетов) "commonMethods" поле "бесплатно" для каждого из 5 или 6 подклассов.

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

.
2
ответ дан 8 December 2019 в 02:21
поделиться

Идея состоит в том, чтобы написать простой текстовый парсер для проверки существования методов. Включить его в качестве задачи в Ant. Пока вы настаиваете на каком-то стандарте кодирования, это должен сделать какой-нибудь простой текстовый парсер, т.е. просто искать отформатированную сигнатуру в нужных исходных файлах.

0
ответ дан 8 December 2019 в 02:21
поделиться

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

Для повышения производительности следует использовать локальное распределение, например, создание функции. Интерпретатор Python хранит локальные переменные в массиве с гораздо более быстрым доступом.
Однако следует отметить, что это деталь реализации CPython; Я подозреваю, что IronPython, например, приведет к совершенно другому результату.

Наконец, для получения дополнительной информации по этой теме, я предлагаю вам интересное эссе от GvR, об оптимизации в Python: Python Patterns - An Optimization Anecdote .

-121--1869998-

Самый простой способ справиться с этим - использовать C + +/CLI и выставлять логику как типы .NET.

Встроить собственный класс C++ в ref class , который можно использовать непосредственно из пользовательского интерфейса C #, очень просто.

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

Мой опыт заключается в том, что скорость не была проблемой - небезопасный код C # почти всегда удавалось быть так же быстро или быстрее, чем равнозначный C++ при настройке, и это легче профилировать и настроить код C #.

-121--4716752-

В комментарии вы написали "Да, это весь пункт. Я знаю, что их можно назвать разными, но я не хочу, чтобы они были ".

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

Если вы действительно хотите сделать это, вы можете создать пользовательскую Java Annotation для вашего класса, а затем написать Annotation processor и вызвать apt как часть процесса сборки.

Как я сказал много работы, но это может быть полезно, если вы хотите узнать, как аннотации работают.


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

public @interface Car { }

Тогда в вашем процессоре аннотаций вы можете проверить, чтобы автомобиль имеет правильные частные методы.

Я написал свои собственные аннотации, но я проверил их во время выполнения, используя API отражения, а не во время сборки. На самом деле они довольно легкие.

0
ответ дан 8 December 2019 в 02:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: