在 Java 中将大 类 与内部 类 拆分
Splitting big classes with Inner classes in Java
我正在做一个 Android 项目。我到处搜索,但找不到拆分和打包代码的好策略。
我的问题是我有内部 类 使用主要 class 变量,我无法弄清楚 如何解耦它们.
我尝试创建辅助程序 classes,但是要么我通过构造函数传递了很多变量,要么公开了我的主要 class,但我没有我不想做任何一个。
我想将每个 class 的最大代码行数保持在 150。目前是 278。我正在寻找 解耦这些 的想法,具体来说,如何重构 classes 以保留抽象(private
变量)。 Java
这方面的最佳做法是什么?
例如,here 是我的主要 classes 之一,MainActivity
,约 300 行。
把你内心的类拿出来,在它们的构造函数中将MainActivity本身的实例传递给它们。
MainActivity mainActivity;
DownloadJSON(MainActivity mainActivity) {
super();
mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setCancelable(false);
this.mainActivity=mainActivity;
}
在mainActivity中创建变量public,你可以这样访问它们:
// Extract the metadata
mainActivity.pageCount =Integer.parseInt(metaData.get("PAGE_COUNT"));
编辑:
在为 MainActivivty
添加实际代码后,我建议如下:
- 遵循MVC/MVP 架构模式。你可以在我最后写的模板中找到一个 link,但还有更多的模板 - 只需选择一个你喜欢的。一旦你了解了如何在
MainActivity
之外获取所有 UI 相关代码,方法 addButtons()
将消失,以及 CategoriesListener
class.
AllPostsFetchAsyncTask
真的没必要做内推class。在 activity 之外将其作为常规 class 实施。为了将数据从这个 class 传递回 MainActivity
,只需定义一个监听器接口,你的 MainActivity
将实现,并将 MainActivity.this
传递给构造函数 - 当这个任务完成后,它将调用 MainActivity
上的回调方法,该回调方法将处理与 Adapter
的数据绑定。事实上,你在这里采用了一个非常糟糕的做法 - 通过让 AllPostsFetchAsyncTask
了解 MainActivity
的实现细节,你在两者之间创建了不必要的耦合,从而违反了 OOP 的封装、单一职责和开放封闭原则。
只需执行上面的两个步骤,您就可以使这个特定的 MainActivity
方法少于 150 行代码。
您说的是,您打算让活动保持 150 行的长度限制太多。这归结为这样一个事实,即如果您的 Activity
或 Fragment
不是微不足道的,那么一旦您实施了 onCreate()
、onPause()
、onResume()
、onPrepareOptionsMenu()
、onBackStackChanged()
和其他标准生命周期方法,那么在添加自定义控制器的逻辑之前,您的代码可能会超过 150 行。
现在,我完全讨厌内心的 classes 并且我想不惜一切代价避免它们。以下清单可以作为指导,但无论如何都不完整:
- 永远不要操作 controllers/presenters 中的 UI 元素(
Activities
、Fragments
和 Adapters
)——将这些操作封装在单独的 class 中.这些 class 是 MVC/MVP 视图(相对于 Android View),我将它们放在 views
或 mvcviews
包中。我的 Activities
和 Fragments
在它们的源代码中往往有零个 findViewById()
调用。
- 将所有
Adapters
放在一个单独的包中(即使它们有 30 行长)。我称这个包为 controllers.adapters
或 controllers.listadapters
- 如果您需要在您的应用程序中传递一组相关数据 - 定义一个 POJO(也称为值对象)并使用它来封装这些数据。我通常有一个名为
pojos
的包,即使它只包含一个 class.
定义抽象 classes 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 的讨论,但我仍然鼓励你尝试我在这个 template/tutorial 项目中提出的实现:https://github.com/techyourchance/android_mvc_template
希望对您有所帮助
如果内部classes只是访问字段,那么引入一个新的Container class你的MainActivity
class的所有相关字段(你当然可以也可以制作两个或三个小容器,而不是一个大容器。
然后您的示例可以修改为:
/** 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 存根来编译代码:
class Foo {
public Foo() {}
public Foo(Bar bar) {}
}
class Bar {
public void setSomethingTo(String something) {}
}
class Bob {
static String getSomething() {return "Something";}
}
如果内部 classes 也在访问方法,那么您可以在接口中指定那些,由 MainActivity
实现。仅使用接口将 MainActivity
的实例交给其他 classes。这样您就不必公开完整的 MainActivity
并且可以避免循环依赖。
这是部分问题的答案。如问题所述
I have tried to create helper classes, but then either I pass a lot of variables through constructors
这与 Telescoping constructor 非常相似。所以,为了解决这个问题,我个人会使用类似于 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-client 需要记住更多方法时,上述解决方案可能会使您的 class 看起来冗长。因此,为此我想对上述模式稍作修改。为每个 属性 分配密钥也会让 class-client 的事情变得更简单。
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;
}
}
}
首先,根据您的 Activity 实施,您错过了一些关于活动的重要事项。
1.仅 使用静态内部 classes 或独立 classes 用于 AsyncTasks:参见 Background task, progress dialog, orientation change - is there any 100% working solution?
重要的是:
步骤 #2:让 AsyncTask 通过数据成员保持 Activity,通过构造函数和 setter.[=14 进行设置=]
步骤 #5:在 onCreate() 中,如果 getLastNonConfigurationInstance() 不为 null,则将其投射到您的 AsyncTask class 并调用您的 setter将您的新 activity 与任务相关联。
您会注意到,您必须根据 Android 的生命周期方法注册和注销您的组件。了解这一点很重要,始终遵循 Android 生命周期!
记住这一点将始终引导您找到有关解耦 Android 方式的正确答案。
2。在需要时使用 数据保持 classes。
这里不属于 Activity:
// Stores the fetched dataMap
ArrayList<HashMap<String, String>> arrayList;
当你的 Activity 被摧毁时,例如在配置更改期间,您的所有数据都消失了,您需要重新加载所有内容。
可以通过多种不同的方式访问和存储您的数据:http://developer.android.com/guide/faq/framework.html#3
在您的情况下,这可能适用:
A public静态field/method
另一种跨 Activities/Services 访问数据的方法是使用 public 静态字段 and/or 方法。
您可以从您的任何其他 class 访问这些静态字段
应用。要共享对象,创建您的 activity
object 设置一个静态字段指向这个对象和任何其他
activity 想要使用这个对象只是访问这个静态
字段.
还要考虑将您的数据存储在数据库中或通过其他方式,这样即使您的应用程序被销毁,您的数据也不会消失。
3。与您的 Activity 的交流 可以这样完成:http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
以相同的方式将它用于您的视图和视图侦听器。有一个组件管理您的视图(就像片段一样),将其注册到您的 Activity,使用它,在不需要时或生命周期需要时注销它。
正如 1. 中所说,Android 生命周期是一切的关键。
4.依赖注入 是一个非常重要的主题,您可以使用它的框架(如 Dagger 2 或 RoboGuice)或按照您自己的方式进行。确保你的 Injector 知道依赖关系(比如哪些 Buttons 需要哪些 ClickListeners 和 Information 或者你的 Adapter 需要哪些数据)并将它们绑定在一起。当总是考虑生命周期时,你会看到你需要哪些接口和方法以及何时调用它们。
5.不用担心代码行数。如果你的设计是一致的并且有意义,那么即使有 500 行你也不会有可读性问题。顺便提一句。当正确记录您的代码时,它很容易超过 150 行代码。所以,又要担心了。
如果您对实施细节有任何具体问题,请提出具体问题,否则您会得到一个臃肿的答案。
我正在做一个 Android 项目。我到处搜索,但找不到拆分和打包代码的好策略。
我的问题是我有内部 类 使用主要 class 变量,我无法弄清楚 如何解耦它们.
我尝试创建辅助程序 classes,但是要么我通过构造函数传递了很多变量,要么公开了我的主要 class,但我没有我不想做任何一个。
我想将每个 class 的最大代码行数保持在 150。目前是 278。我正在寻找 解耦这些 的想法,具体来说,如何重构 classes 以保留抽象(private
变量)。 Java
这方面的最佳做法是什么?
例如,here 是我的主要 classes 之一,MainActivity
,约 300 行。
把你内心的类拿出来,在它们的构造函数中将MainActivity本身的实例传递给它们。
MainActivity mainActivity;
DownloadJSON(MainActivity mainActivity) {
super();
mProgressDialog = new ProgressDialog(MainActivity.this);
mProgressDialog.setCancelable(false);
this.mainActivity=mainActivity;
}
在mainActivity中创建变量public,你可以这样访问它们:
// Extract the metadata
mainActivity.pageCount =Integer.parseInt(metaData.get("PAGE_COUNT"));
编辑:
在为 MainActivivty
添加实际代码后,我建议如下:
- 遵循MVC/MVP 架构模式。你可以在我最后写的模板中找到一个 link,但还有更多的模板 - 只需选择一个你喜欢的。一旦你了解了如何在
MainActivity
之外获取所有 UI 相关代码,方法addButtons()
将消失,以及CategoriesListener
class. AllPostsFetchAsyncTask
真的没必要做内推class。在 activity 之外将其作为常规 class 实施。为了将数据从这个 class 传递回MainActivity
,只需定义一个监听器接口,你的MainActivity
将实现,并将MainActivity.this
传递给构造函数 - 当这个任务完成后,它将调用MainActivity
上的回调方法,该回调方法将处理与Adapter
的数据绑定。事实上,你在这里采用了一个非常糟糕的做法 - 通过让AllPostsFetchAsyncTask
了解MainActivity
的实现细节,你在两者之间创建了不必要的耦合,从而违反了 OOP 的封装、单一职责和开放封闭原则。
只需执行上面的两个步骤,您就可以使这个特定的 MainActivity
方法少于 150 行代码。
您说的是,您打算让活动保持 150 行的长度限制太多。这归结为这样一个事实,即如果您的 Activity
或 Fragment
不是微不足道的,那么一旦您实施了 onCreate()
、onPause()
、onResume()
、onPrepareOptionsMenu()
、onBackStackChanged()
和其他标准生命周期方法,那么在添加自定义控制器的逻辑之前,您的代码可能会超过 150 行。
现在,我完全讨厌内心的 classes 并且我想不惜一切代价避免它们。以下清单可以作为指导,但无论如何都不完整:
- 永远不要操作 controllers/presenters 中的 UI 元素(
Activities
、Fragments
和Adapters
)——将这些操作封装在单独的 class 中.这些 class 是 MVC/MVP 视图(相对于 Android View),我将它们放在views
或mvcviews
包中。我的Activities
和Fragments
在它们的源代码中往往有零个findViewById()
调用。 - 将所有
Adapters
放在一个单独的包中(即使它们有 30 行长)。我称这个包为controllers.adapters
或controllers.listadapters
- 如果您需要在您的应用程序中传递一组相关数据 - 定义一个 POJO(也称为值对象)并使用它来封装这些数据。我通常有一个名为
pojos
的包,即使它只包含一个 class. 定义抽象 classes
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 的讨论,但我仍然鼓励你尝试我在这个 template/tutorial 项目中提出的实现:https://github.com/techyourchance/android_mvc_template
希望对您有所帮助
如果内部classes只是访问字段,那么引入一个新的Container class你的MainActivity
class的所有相关字段(你当然可以也可以制作两个或三个小容器,而不是一个大容器。
然后您的示例可以修改为:
/** 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 存根来编译代码:
class Foo {
public Foo() {}
public Foo(Bar bar) {}
}
class Bar {
public void setSomethingTo(String something) {}
}
class Bob {
static String getSomething() {return "Something";}
}
如果内部 classes 也在访问方法,那么您可以在接口中指定那些,由 MainActivity
实现。仅使用接口将 MainActivity
的实例交给其他 classes。这样您就不必公开完整的 MainActivity
并且可以避免循环依赖。
这是部分问题的答案。如问题所述
I have tried to create helper classes, but then either I pass a lot of variables through constructors
这与 Telescoping constructor 非常相似。所以,为了解决这个问题,我个人会使用类似于 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-client 需要记住更多方法时,上述解决方案可能会使您的 class 看起来冗长。因此,为此我想对上述模式稍作修改。为每个 属性 分配密钥也会让 class-client 的事情变得更简单。
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;
}
}
}
首先,根据您的 Activity 实施,您错过了一些关于活动的重要事项。
1.仅 使用静态内部 classes 或独立 classes 用于 AsyncTasks:参见 Background task, progress dialog, orientation change - is there any 100% working solution?
重要的是:
步骤 #2:让 AsyncTask 通过数据成员保持 Activity,通过构造函数和 setter.[=14 进行设置=]
步骤 #5:在 onCreate() 中,如果 getLastNonConfigurationInstance() 不为 null,则将其投射到您的 AsyncTask class 并调用您的 setter将您的新 activity 与任务相关联。
您会注意到,您必须根据 Android 的生命周期方法注册和注销您的组件。了解这一点很重要,始终遵循 Android 生命周期!
记住这一点将始终引导您找到有关解耦 Android 方式的正确答案。
2。在需要时使用 数据保持 classes。
这里不属于 Activity:
// Stores the fetched dataMap
ArrayList<HashMap<String, String>> arrayList;
当你的 Activity 被摧毁时,例如在配置更改期间,您的所有数据都消失了,您需要重新加载所有内容。
可以通过多种不同的方式访问和存储您的数据:http://developer.android.com/guide/faq/framework.html#3
在您的情况下,这可能适用:
A public静态field/method
另一种跨 Activities/Services 访问数据的方法是使用 public 静态字段 and/or 方法。 您可以从您的任何其他 class 访问这些静态字段 应用。要共享对象,创建您的 activity object 设置一个静态字段指向这个对象和任何其他 activity 想要使用这个对象只是访问这个静态 字段.
还要考虑将您的数据存储在数据库中或通过其他方式,这样即使您的应用程序被销毁,您的数据也不会消失。
3。与您的 Activity 的交流 可以这样完成:http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
以相同的方式将它用于您的视图和视图侦听器。有一个组件管理您的视图(就像片段一样),将其注册到您的 Activity,使用它,在不需要时或生命周期需要时注销它。
正如 1. 中所说,Android 生命周期是一切的关键。
4.依赖注入 是一个非常重要的主题,您可以使用它的框架(如 Dagger 2 或 RoboGuice)或按照您自己的方式进行。确保你的 Injector 知道依赖关系(比如哪些 Buttons 需要哪些 ClickListeners 和 Information 或者你的 Adapter 需要哪些数据)并将它们绑定在一起。当总是考虑生命周期时,你会看到你需要哪些接口和方法以及何时调用它们。
5.不用担心代码行数。如果你的设计是一致的并且有意义,那么即使有 500 行你也不会有可读性问题。顺便提一句。当正确记录您的代码时,它很容易超过 150 行代码。所以,又要担心了。
如果您对实施细节有任何具体问题,请提出具体问题,否则您会得到一个臃肿的答案。