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

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

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

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

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

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

5 ответов


Edit:

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

  1. следуйте архитектурному шаблону MVC/MVP. Вы можете найти ссылку на шаблон, который я написал в конце, но есть много шаблонов - просто выберите тот, который вам нравится. Как только вы поймете, как получить весь код, связанный с UI, за пределами MainActivity метод addButtons() исчезнет, как и CategoriesListener класса.
  2. там действительно нет необходимости 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 посмотреть) и я положил их в views или mvcviews пакеты. Мой Activities и Fragments как правило, имеют нулевой findViewById() вызовы в исходном коде.
  • поставить все Adapters в отдельном пакете (даже если они 30 строк). Я называю этот пакет controllers.adapters или controllers.listadapters
  • Если вам когда - либо нужно передать набор связанных данных в вашем приложении-определите POJO-класс (также известный как объект Value) и использовать его для инкапсуляции этих данных. Обычно у меня есть пакет с именем pojos, даже если он содержит только один класс.
  • определить абстрактные классы AbstractActivity и AbstractFragment и поместите туда любую логику удобства, которая используется вашими контроллерами. Например: у меня всегда есть следующий метод (или аналогичный) в my 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

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


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

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

это важно:

Шаг #2: удерживайте AsyncTask на активности через элемент данных, установленный через конструктор и наладчик.

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

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

вспоминая это всегда привести вас к правильным ответам относительно развязки Android путь.

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

Это здесь на самом деле не относится к деятельности:

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

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

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

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

  • открытое статическое поле/метод

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

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

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

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

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

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

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

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


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

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

Это что-то очень похожее на телескопическая конструктор. Итак, чтобы решить эту проблему, я бы лично использовал что-то похожее на шаблон 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;
     }     

   }
}

если внутренние классы имеют доступ только к полям, то введите новый класс контейнера всех соответствующих полей вашего 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 и вы можете избежать циклических зависимостей.


возьмите свои внутренние классы, передайте им экземпляр 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"));