Java:类中的所有东西都是静态的 - 这是合理的吗?

时间:2013-05-20 02:35:50

标签: java

我只是想知道我所做的事情是不是很糟糕。

我有一个ArrayList的东西。我需要这个列表永远存在。我只需要有一个这样的清单。我也有一些方法可以与这个列表进行交互。因此,我让一切都变得静止。

问题在于,由于所有这些东西都隐藏在一个类中,因此该类中的所有内容都被声明为静态。这似乎有点奇怪,因为我想让整个课程都是静态的。

Java不允许我使整个类静态并且我被教导在我的代码中最小化静态方法的事实引起了我脑子里的一些警钟,但老实说我看不出任何理性为什么我正在做的事情不会很好。

编辑:关于该计划的更多信息以及为什么我决定做我所做的事情,因为我猜这会有所帮助(当然也有人问过)。

程序的中心是两个数据库,一个用于项目,另一个用于字符。 角色需要暂时拥有项目,但所有项目必须始终列出。

我决定我会有一个项目的arraylist,每个项目都有一个布尔标记可用或不可用(可以很容易地显示所有项目和可用项目)。每个角色都有自己的,较小的项目arraylist,我将从数据库中添加项目的副本。

为了能够从其他类访问数据库(这是我从这个想法开始的地方),我认为我最简单的选择是简单地使大型arraylist静态,因为没有我不需要它存在的情况那里不是我需要不止一个的情况。当然,当我将列表设置为静态时,我还需要使用静态交互的所有基本方法。

我很确定有更好的方法来做我想做的事情,但我只是一个初学者练习。

EDIT2:哦,项目列表将被添加到,删除,以及程序运行时修改的项目。接收物品副本的角色的另一个影响是,只要拥有物品,他们自己的物品就会保持不变。

4 个答案:

答案 0 :(得分:4)

如果您的系统中只需要一个Thing列表,则需要问问自己这些是什么。它们是在每次运行之前可以配置的项目吗?它们会随着用户执行程序而改变吗?也许这些东西应该作为记录存储在数据库中,甚至是像Apache Derby或NoSQL数据库那样的轻量级内存中。

即使你真的拥有一组固定的项目,你也应该考虑使用依赖注入系统并在单例范围内出售列表,而不是使用硬连线单例。这样,您可以通过更改配置来替换测试类中的列表。

如果你发现最后一个令人困惑,请考虑这一点。假设你有一个处理Things的类,比如在它里面保留一个静态列表。类似的东西:

public class ThingCatalog {
    private static final List<Thing> things = new ArrayList<>();
    public ThingCatalog() {
        // initialize list of things.
    }
    public List<Thing> getThings() {
        return things;
    }
    public Thing getThingWithId(int id) {
        // ...
    }
}

现在,你可以让ThingCatalog成为单身人士;你已经看到了如何做到这一点。使构造函数为私有,并创建静态getInstance方法。你很想写

public class TreasureGenerator {
    private ThingCatalog things = ThingCatalog.getInstance();
    // ...
}

如果你想在这个类中使用不使用东西的方法进行写单元测试,会发生什么?你不需要这些东西,所以你根本不需要ThingCatalog。不幸的是,你坚持不懈。

您可以通过为TreasureGenerator提供setThingCatalog方法来解决此问题:

public void setThingCatalog(ThingCatalog things) {
   this.things = things;
}

当然,你只有一个ThingCatalog,所以这没什么用。但是如果你有一个ThingCatalog可以实现的界面:

public interface ThingVendor {
    List<Thing> getThings();
    Thing getThingById(int id);
}

并且所有类都使用ThingVendor而不是ThingCatalog,您可以在测试中替换它。

这是一个更像商业的例子。您正在编写财务计划,并且需要在今天的支票上打印。典型的是编写如下代码:

String recipient = ...;
String accountOwner = ...;
BigDecimal amount = ...;
String accountNumber = ...;
Date today = new Date();
printCheck(...);

现在,有人问你“你的程序可以正确处理闰日吗?”十四年前,问题可能是关于Y2K。你会怎么测试这个?你在这种方法中坚持使用今天

相反,您编写了一个名为DateGenerator的接口:

public interface DateGenerator {
    Date today();
}

public class TodayGenerator implements DateGenerator {
    public Date today() { return new Date(); }
}

public class LeapDayGenerator implements DateGenerator {
    public Date today() {
        Calendar cal = Calendar.getInstance();
        cal.set(2016, FEBRUARY, 29); // assume static imports;
        return cal.getTime();
    }
}

你的类将有一个setDateGenerator方法,你可以正常使用TodayGenerator。在闰日测试中,您将使用LeapDayGenerator。

依赖注入系统可自动执行这些过程。如果你坚持使用计算,你将学到的经验是,对象不应该知道如何配置自己。项目的其他部分应该将对象粘合在一起。

答案 1 :(得分:2)

很抱歉,但我担心你的计划可能不好,而且你可能需要重新设计你的程序。你的班级有一个州,这告诉我它不应该是静态的。

应该是静态的事情:

  • 常量
  • 效用方法
  • 属于该类的字段,而不是该实例。

考虑向我们提供有关您的计划结构的更多信息,以便我们提供更准确的答案。

答案 2 :(得分:2)

根据您的更新,您已经达到了经典的“它是单身人士!”那一刻,有人应该向你指出“它(几乎)从来不是一个单身人士!”。相反,这应该是一个普通的,非静态的非单例类,应该编写应用程序的其余部分以始终使用它的单个实例。您的应用程序只需要一个事物的单个实例这一事实并不会使该事物成为单例。您可以阅读Google搜索"why is singleton evil""singleton antipattern"中的几篇文章。

听起来你可能正在谈论共享的可变状态,这是一个你不想在没有经过仔细考虑的情况下进入的其他(巨型)蠕虫病毒。

答案 3 :(得分:1)

我倾向于同意你的设计听起来不是最理想的。您的许多目标似乎都是典型的程序性目标,而不是面向对象的程序。

如果您发现您的项目非常依赖静态可变数据,那么您应该问问自己为什么需要这么多全局数据。