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

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

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

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

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

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

Solutions Collecting From Web of "Разделение больших классов на внутренние классы в Java"

Редактировать:

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

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

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

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

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

  • Никогда не манипулируйте элементами пользовательского интерфейса в контроллерах / презентаторах ( Activities , Fragments and 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 } 
  • Проверьте, есть ли какие-либо сторонние библиотеки, которые могут быть полезны в контексте вашего приложения и использовать их.

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

Введите описание изображения здесь

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

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

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

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

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

Шаг № 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 строк кода. Итак, снова о чем беспокоиться.

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

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

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

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

 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 класс 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 и вы можете избежать круговых зависимостей.

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