该应用程序是一个学生管理系统,使用.csv文件作为我们学校项目的一部分。
这是我第一次创建一个基本的DAO。我想得到一些反馈,说明到目前为止可以改进什么,以及如何为UserDAO.class编写好的单元测试。
package com.studentenverwaltung.persistence;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.supercsv.cellprocessor.constraint.NotNull;
import org.supercsv.cellprocessor.constraint.Unique;
import org.supercsv.cellprocessor.ift.CellProcessor;
import org.supercsv.io.CsvBeanReader;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanReader;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;
import com.studentenverwaltung.model.User;
/**
*
* @author philippkosel
*
*/
public class UserDAO {
private static final String CSV_FILE = "/com/studentenverwaltung/resources/test.csv";
private ArrayList<User> users;
private URL url;
/**
*
*/
public UserDAO() {
this.users = new ArrayList<User>();
this.url = getClass().getResource(this.CSV_FILE);
}
/**
*
* @return
*/
private static CellProcessor[] getProcessors() {
final CellProcessor[] processors = new CellProcessor[] { new Unique(), // id
new NotNull() // password
};
return processors;
}
/**
*
* @throws IOException
*/
private void readCSV() throws IOException {
ICsvBeanReader beanReader = null;
try {
beanReader = new CsvBeanReader(new FileReader(this.url.getPath()),
CsvPreference.STANDARD_PREFERENCE);
final String[] header = beanReader.getHeader(true);
final CellProcessor[] processors = getProcessors();
User newUser;
while ((newUser = beanReader.read(User.class, header, processors)) != null) {
this.users.add(newUser);
}
} finally {
if (beanReader != null) {
beanReader.close();
}
}
}
/**
*
* @throws IOException
*/
private void writeCSV() throws IOException {
ICsvBeanWriter beanWriter = null;
try {
File temp = File.createTempFile("tempfile", ".tmp");
beanWriter = new CsvBeanWriter(new FileWriter(temp),
CsvPreference.STANDARD_PREFERENCE);
final String[] header = new String[] { "id", "password" };
final CellProcessor[] processors = getProcessors();
beanWriter.writeHeader(header);
for (User user : this.users) {
beanWriter.write(user, header, processors);
}
File file = new File(this.url.getPath());
file.delete();
temp.renameTo(file);
} finally {
if (beanWriter != null) {
beanWriter.close();
}
}
}
/**
*
* @param user
* @throws IOException
*/
public void add(User user) throws IOException {
readCSV();
this.users.add(user);
writeCSV();
}
/**
*
* @param user
* @throws IOException
*/
public void update(User user) throws IOException {
readCSV();
for (User userToBeUpdated : this.users) {
if (userToBeUpdated.getId().equals(user.getId())) {
this.users.remove(userToBeUpdated);
this.users.add(user);
}
}
writeCSV();
}
/**
*
* @param userId
* @throws IOException
*/
public void delete(String userId) throws IOException {
readCSV();
for (User userToBeDeleted : this.users) {
if (userToBeDeleted.getId().equals(userId)) {
this.users.remove(userToBeDeleted);
}
}
writeCSV();
}
/**
*
* @return
* @throws IOException
*/
public List<User> findAll() throws IOException {
readCSV();
return this.users;
}
/**
*
* @param userId
* @return
* @throws IOException
*/
public User findByPrimaryKey(String userId) throws IOException {
readCSV();
for (User userToBeReturned : this.users) {
if (userToBeReturned.getId().equals(userId)) {
return userToBeReturned;
}
}
return null;
}
}package com.studentenverwaltung.persistence;
import org.junit.Test;
import com.studentenverwaltung.model.User;
public class UserDAOTest {
@Test
public void testAdd() {
UserDAO ud = new UserDAO();
User user = new User();
user.setId("test");
user.setPassword("test");
ud.add(user);
}
}发布于 2013-05-04 14:45:51
看起来在writeCSV()中有一个潜在的错误。打开一个临时文件并写入它,然后在关闭它之前移动该文件。
try {
File temp = File.createTempFile("tempfile", ".tmp");
beanWriter = new CsvBeanWriter(new FileWriter(temp), CsvPreference.STANDARD_PREFERENCE);
// ...
temp.renameTo(file); // <-- moving a file that hasn't been closed yet!
} finally {
if (beanWriter != null) {
beanWriter.close();
}
}您目前正在以类路径资源的形式访问该文件:
getClass().getResource(this.CSV_FILE);虽然这在开发中可能有效,但当将应用程序捆绑到jar和/或更大的应用程序中时,不太可能起作用。该文件很可能驻留在jar中,这可能会阻止将其作为File进行读取/写入。我建议使用不同的策略来存储和定位文件。
CSV_FILE被声明为static成员。这意味着它属于类本身,而不是类的任何特定实例。您正在以非静态的方式访问它。以静态的方式访问它更常见,例如:
this.url = getClass().getResource(CSV_FILE);
this.url = getClass().getResource(UserDAO.CSV_FILE);由于它是私有的(因此,只在这个类中引用),我倾向于选择第一个。
users成员的使用对我来说似乎很奇怪。它不用于维护任何单一方法之外的状态。我看不出有什么令人信服的理由让它成为这个班的一员。考虑将readCSV()和writeCSV()更改为分别返回和接受Collection of User,并将users本地化到需要它的方法。
private Collection<User> readCSV() throws IOException { ... }
private void writeCSV(Collection<User> users) throws IOException { ... }其他方法可以这样做:
public void add(User user) throws IOException {
Collection<User> users = readCSV();
users.add(user);
writeCSV(users);
}这使得通过限制users的范围来读取这些方法变得更加容易。不再有任何状态可以跟踪该方法范围之外的情况。
注意,我也在使用Collection而不是List或ArrayList。对我来说,List的使用意味着用户处于某种定义的顺序中。在这段代码中,我没有看到任何强制执行任何特定顺序的内容,所以我选择了使用Collection。您应该使用适当的集合类型来显示含义/意图。如果订单重要且有意义,请坚持使用List。如果订单不重要,请使用Collection。Set也是一种选择。Set表示顺序不重要,但唯一性重要。
发布于 2013-05-04 16:40:11
对不起,拼写和语法问题,我在平板电脑上。
至于单元测试,您应该始终先进行测试。把它想象成一次学校考试。你的老师给你一张白纸,告诉你你还有30分钟要完成吗?单元测试是测试中的问题。你的生产代码就是答案。你的老师正在检查你的答案。
因此,通过您的测试,我想如何拯救我的用户,如何将我的用户加载到内存中,我应该如何将更多的用户添加到我的collection...etc中,一旦您编写了这些问题,然后编写您的代码就可以了。现在,如果您忘记了一个方法是如何工作的,那么现在可以回到您的单元测试,看看如何实现它。我希望这幅插图能给你一个正确的方向。
https://codereview.stackexchange.com/questions/25788
复制相似问题