Java 用于在集合或列表和其他对象中保存值的接口

Java Interface for saving values in Sets or Lists and other Objects

我想编写一个程序,用户可以在其中决定在哪个对象中存储值。用户可以使用集合、列表和文件(例如.txt、.xml)。我想编写一个界面,最终用户选择哪个(“存储”)对象并不重要,这样我就不必为每个决定都编写相同的方法。

我应该如何为它制作一个界面?使用界面的方法是否足够合适,我还需要什么 do/consider?

import java.io.File;
public class StoreValues implements SaveInGeneral<SomeObject>{

      //user's decision (LinkedList, Set or File)

      if(decision == 1){
          SaveInGeneral<SomeObject> obj = new LinkedList<>();
      }
      if(decision == 2){
           SaveInGeneral<SomeObject> obj = new File();
      }
      //...

      obj.add(someObject);

}    

SaveInGeneral 不符合常见的命名策略,其中涉及尝试用名词命名事物。例如,我称之为 Storage

泛型在这里似乎没有用 - 重点是抽象出底层存储机制是什么。所以摆脱它。

然后,定义 'save an object' 的确切含义。例如,一个 List 可以存储项目 (.add(newItem)),但你可以通过索引检索项目 (.get(5)),创建一个迭代器 (with .iterator()) 这样你就可以 for (String v : list)通过它,并询问它的大小(.size())等

您在寻找哪种基元?

大概如果所有这些只是存储对象而不是其他任何东西,那么您正在寻找的唯一方法是 .store(Object o)

问题是,任务:“在磁盘上存储任意对象”根本不起作用。大多数对象根本无法存储到磁盘。我强烈建议您将 .store() 方法限制为仅允许您知道如何存储的内容。您可以使用 Serializable,但那是一大堆蠕虫(可序列化非常复杂),或者您需要涉及第三方库,例如 Jackson,它们试图将对象编组为例如JSON 或 XML.

然后您需要考虑各种目标平台(文件、数据库、列表、集合等)的需求,并将其与需要存储内容的代码的需求相比较。找到子集,使其仅包含可在 all 目标存储机制中实现的内容,and 这足以满足您需要的代码存储后端。

这会很快变得复杂。例如,当读出 Jackson 生产的 JSON 时,您需要提供要将 JSON 读入哪个 class,这不是列表需要的东西(他们知道哪种对象他们已经存储了)。反过来,文件不喜欢你继续写入一小块数据,然后关闭文件,然后再次打开它 - 开销意味着:

  • 循环 1000 次并执行:打开文件,写入约 50 字节的数据,关闭文件。

实际上比

慢了大约 1000 倍
  • 打开文件,循环1000次,执行:写入50字节数据。然后关闭文件。

换句话说,您必须更新 API 以包含打开和关闭步骤,或者接受基于文件的存储后端非常慢 (1000 倍)。

这是最简单的做法 - 让我们只存储字符串,因为它很容易发送到从数据库到文件到列表到网络套接字的任何东西,让我们暂时接受一个低效的算法:

public interface Storage {
    public void store(String data) throws IOException;
}

一些实现:

public class ListBasedStorage implements Storage {
    private final List<String> list = new ArrayList<String>();

    public List<String> getBackingList() {
        return list;
    }

    public void store(String data) {
        list.add(data);
    }
}

public class FileBasedStorage implements Storage {
    private final Path target;
    private static final Charset CHARSET = StandardCharsets.UTF_8;

    public FileBasedStorage(Path p) {
        this.target = target;
    }

    public void store(String data) throws IOException {
        String line = data.replaceAll("\R", " ") + "\n";
        Files.write(target, line, CHARSET, StandardOpenOption.APPEND);
    }
}

并使用它:

public static void main(String[] args) throws Exception {
  Storage storage = new FileBasedStorage(Paths.get("mydata.txt"));
  new MyApp().sayHello(storage);
}

public void sayHello(Storage storage) throws IOException {
  storage.store("Hello!");
  storage.store("World");
}

然后您可以通过添加更多数据类型或使用例如JSON 和像 jackson 这样的 JSON 编组器将这些数据转换成你可以放入文件或数据库中的东西,添加检索代码(存储也可以询问包含多少条目,例如询问一个遍历数据的迭代器,等等),并添加一个 2 层的方法,你要求存储一个 'session',必须使用 try (Session session = storage.start()) {} 安全地关闭它),以便有快速的文件和数据库写入(文件和数据库都是事务性的,从某种意义上说,如果你明确地开始,做一些事情,然后保存你刚刚所做的一切,它们会工作得更好)。