Разделение больших классов на внутренние классы в Java

Я работаю над проектом Android. Я искал высокий и низкий, но я не могу найти хорошую стратегию для разделения и упаковки моего кода.

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

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

Я хочу сохранить максимальные строки кода для каждого класса до 150. В настоящее время он 278. Я ищу идеи для развязки этих, в частности, как реструктурировать классы для сохранения абстракции (private переменные). Каковы наилучшие методы Java для этого?

В качестве примера здесь один из моих основных классов, MainActivity, ~ 300 строк.

Ответы

Ответ 1

Edit:

После добавления фактического кода для MainActivivty я предлагаю следующее:

  • Следуйте архитектурному шаблону MVC/MVP. Вы можете найти ссылку на шаблон, который я написал в конце, но есть еще много шаблонов - просто выберите тот, который вам нравится. Как только вы поймете, как получить весь код, связанный с пользовательским интерфейсом, за пределами MainActivity, метод addButtons() исчезнет, ​​а также класс CategoriesListener.
  • Нет необходимости, чтобы AllPostsFetchAsyncTask был внутренним классом. Реализовать его как обычный класс за пределами деятельности. Чтобы передать данные из этого класса обратно в MainActivity, просто определите интерфейс прослушивателя, который будет реализован вашим MainActivity, и передайте MainActivity.this конструктору - когда эта задача будет завершена, он вызовет метод обратного вызова на MainActivity, который, в свою очередь, будет обрабатывать привязку данных к Adapter. Фактически, вы принимаете здесь очень плохую практику - делая AllPostsFetchAsyncTask осведомленным о деталях реализации MainActivity, вы создаете ненужную связь между ними, нарушая тем самым инкапсуляцию, единую ответственность и открытые закрытые принципы ООП.

Просто выполнив два шага выше, вы сделаете этот MainActivity путь менее 150 строк кода.

Сказал, что ваше намерение вести деятельность 150 строк длиннее слишком ограничительно. Это сводится к тому, что если ваши Activity или Fragment не являются тривиальными, то после реализации onCreate(), onPause(), onResume(), onPrepareOptionsMenu(), onBackStackChanged() и других стандартных методов жизненного цикла у вас, вероятно, будет более 150 строк кода, даже если вы добавите логику своего настраиваемого контроллера.

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

  • Никогда не манипулируйте элементами пользовательского интерфейса в контроллерах/презентаторах (Activities, Fragments и Adapters) - инкапсулируйте эти манипуляции в отдельные классы. Эти классы представляют собой MVC/MVP-представления (в отличие от Android View), и я помещаю их в пакеты views или mvcviews. Мои Activities и Fragments имеют в своем исходном коде нулевые findViewById() вызовы.
  • Поместите все Adapters в отдельный пакет (даже если они длиной 30 строк). Я называю этот пакет controllers.adapters или controllers.listadapters
  • Если вам когда-либо понадобится передать набор связанных данных в ваше приложение - определите POJO (также известный как объект Value) и используйте он должен инкапсулировать эти данные. Обычно у меня есть пакет с именем pojos, даже если он содержит только один класс.
  • Определите абстрактные классы AbstractActivity и AbstractFragment и поставьте туда любую удобную логику, используемую вашими контроллерами. Например: у меня всегда есть следующий метод (или аналогичный) в моих AbstractActivity и AbstractFragment:

    public void replaceFragment(Class <? extends Fragment> claz, boolean addToBackStack, Bundle args) { 
        // Code to replace the currently shown fragment with another one 
    }
    
  • Проверьте, нет ли каких-либо сторонних библиотек, которые могут быть полезны в контексте вашего приложения и использовать их.

Моя упаковка обычно следует этой схеме:

enter image description here

Я знаю, что вы написали, что вы уже видели некоторые обсуждения MVC, но я все же рекомендую вам попробовать реализацию, которую я предлагаю в этом проекте шаблона/учебника: https://github.com/techyourchance/android_mvc_template

Надеюсь, что это поможет

Ответ 2

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

1. Только используют статические внутренние классы или автономные классы для AsyncTasks: см. Фоновая задача, диалог прогресса, изменение ориентации - есть ли 100% -ное рабочее решение? p >

Важно следующее:

Шаг №2: Удерживайте AsyncTask в Activity с помощью элемента данных, заданного через конструктор и сеттер.

Шаг # 5: В onCreate(), если getLastNonConfigurationInstance() не является нулевым, переведите его в класс AsyncTask и вызовите ваш сеттер, чтобы связать ваше новое действие с задачей.

Вы заметите, что вам придется регистрироваться и отменить регистрацию своих компонентов на основе методов жизненного цикла Android. Это важно знать, всегда следует жизненному циклу Android!

Помните, что это всегда приведет вас к правильным ответам относительно развязки Android-способа.

2. Используйте классы хранения данных, если это необходимо.

Это не относится к действию:

// Stores the fetched dataMap
ArrayList<HashMap<String, String>> arrayList;

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

Доступ к вашим данным и их сохранение можно сделать разными способами: http://developer.android.com/guide/faq/framework.html#3

В вашем случае это может быть применимо:

  • Публичное статическое поле/метод

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

Также подумайте о сохранении данных insida в базе данных или другими способами, поэтому даже после того, как ваше приложение будет уничтожено, ваши данные не исчезнут.

3. Связь с вашей деятельностью может быть выполнена следующим образом: http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity

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

Как сказано в 1., жизненный цикл Android является ключом ко всему.

4. Инъекция зависимостей - очень важная тема, и вы можете либо использовать для нее фреймворк (например, Dagger 2 или RoboGuice), либо сделать это по-своему. Убедитесь, что ваш инжектор знает зависимости (например, какие кнопки нужны, какие ClickListeners и информация или какие данные требуется вашему адаптеру) и связать их вместе. Когда вы всегда рассматриваете жизненный цикл, вы увидите, какие интерфейсы и какие методы вам нужны, и когда их вызывать.

5. Не беспокойтесь о количестве строк кода. Если ваш дизайн согласован и имеет смысл, у вас не будет проблем с чтением даже с 500 линиями. Btw. при правильном оформлении вашего кода он легко получает более 150 строк кодов. Итак, снова о чем беспокоиться.

Если у вас есть какие-либо конкретные вопросы о деталях реализации, задайте конкретный вопрос или вы получите раздутый ответ.

Ответ 3

Это ответ на часть проблемы. Как указано в вопросе

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

Это нечто очень похожее на Конструктор телескопов. Итак, чтобы решить эту проблему, я лично использовал бы что-то похожее на шаблон Builder.

class A {
  public class B {
     public B(int x, int y, int z, int m, int n, int o){

     }
  }
}

Вышеуказанный случай может быть изменен, как показано ниже.

class A {
   public class B{
     int a, int b, int c, int m, int n, int p = 0;
     public B(){
     }
     public B setA(int x){
       a = x;
       return this;
     }     
     public B setB(int x){
       b = x;
       return this;
     }
     ... and similar methods for other properties.     
   }
}

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

class A {
   public class B{
     int a, int b, int c, int m, int n, int p = 0; // key for int a == "a" and for b is "b" and so on... this is our assumption.
     public B(){
     }
     public B setProperty(String key, int value){
       if(key.equals("a")){
           a = value;
       }else if(key.equals("b")){
           b = value;
       } ... and so on for other properties.
       return this;
     }     

   }
}

Ответ 4

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

Затем ваш пример можно изменить следующим образом:

/** new container class */
class FooBar {
    public Foo foo;
    public Bar bar;
}

/** nice, easy and SHORT! */
class MainActivity {

    private FooBar fooBar;

    public MainActivity() {
        new Ping(fooBar);
        new Pong(fooBar).someMethod();
    }
}

/** successfully converted from inner class to class */
class Ping {

    public Ping(FooBar fooBar) {
        fooBar.foo = new Foo(); // Ping modifies Foo
    }
}

/** successfully converted from inner class to class */
class Pong {

    private Bob bob;
    private FooBar fooBar;

    public Pong (FooBar fooBar) {
        this.fooBar = fooBar;
        fooBar.bar = new Bar(); // Pong modifies bar
        bob = new Bob();
    }

    public void someMethod () {
        fooBar.bar.setSomethingTo(Bob.getSomething()); // Pong modifies bar of Main class
        fooBar.foo = new Foo(fooBar.bar); // Pong assignes something to bar
    }
}

Я использовал эти заглушки класса для компиляции кода:

class Foo {
    public Foo() {}
    public Foo(Bar bar) {}
}
class Bar {
    public void setSomethingTo(String something) {}
}
class Bob {
    static String getSomething() {return "Something";}
}

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

Ответ 5

Выньте свои внутренние классы, передайте им экземпляр MainActivity в своих конструкторах.

MainActivity mainActivity;
DownloadJSON(MainActivity mainActivity) {
        super();
        mProgressDialog = new ProgressDialog(MainActivity.this);
        mProgressDialog.setCancelable(false);
        this.mainActivity=mainActivity;
    }

Сделать переменные в mainActivity общедоступными, и вы можете получить к ним доступ следующим образом:

          // Extract the metadata
         mainActivity.pageCount =Integer.parseInt(metaData.get("PAGE_COUNT"));