gpt4 book ai didi

java - View 和 Controller 之间的封装

转载 作者:行者123 更新时间:2023-12-01 11:19:28 24 4
gpt4 key购买 nike

我正在开展一个学校项目,我们需要创建一个管理任务和项目的基本应用程序。我们的项目遵循 MVC 模式。重点在于应用程序的设计,我们的团队一直在努力解决某个设计问题:

封装 View 和 Controller 之间传递的数据

这意味着我们要确保 View 没有任何对真实数据的引用。我们尝试通过创建值类来解决这个问题,但这是一个巨大的解决方法。这些是最终类,本质上是普通模型类的副本。例如,如果您有一个 Project 类,那么您还将有一个名为 ProjectValue 的最终类,其所有字段与 Project 相同,只是它们是allfinal 使值类的对象不可变,因此 View 中没有任何内容可以更改。复制所有这些类来获得一些额外的封装感觉不太合适,必须有一种更简单的方法。

我将尝试用一个例子来解释这个问题:

用户想要查看所有项目。因此,他将启动应用程序并单击标有“显示项目”的按钮。该按钮将在 Controller 中启动一个名为 getAll() 的方法:

public PList<ProjectValue> getAll()
{
PList<ProjectValue> projects = PList.empty();

for (BranchOffice office : company.getBranchOffices())
{
for (Project project : office.getProjects())
{
projects = projects.plus(project.getValue());
}
}

return projects;
}

首先它循环遍历所有分支机构。对于分支机构中的每个项目,它都会获取项目的值对象 (project.getValue()) 并将其放入列表中,而不是普通项目中。

模型类及其内部值类的示例:

public class Resource implements Serializable, Comparable<Resource> {

/**
* Variable registering the name for this resource.
*/
private String name;

private BranchOffice office;

/**
* Variable registering the resource type for this resource.
*/
private ResourceType type;

/**
* Variable registering the reservations that reserve this resource.
*/
private Set<Reservation> reservations;


/**
* Initializes the resource with a given name and type.
*
* @param name
* The name for the resource, f.e. Audi
* @param type
* The type of the resource, f.e. Car
* @throws InvalidResourceException
*/
public Resource(String name, BranchOffice office, ResourceType type)
throws InvalidResourceException
{
try
{
setName(name);
setBranchOffice(office);
setType(type);
setReservations(null);
} catch (InvalidRequiredStringException
| InvalidRequiredResourceTypeException e)
{
throw new InvalidResourceException(e.getMessage(), this);
}
}

/**
* @return the key
*/
public String getKey() { return name; }
/**
* @return the type
*/
private ResourceType getType() { return type; }
private String getName() { return name; }
public Set<Reservation> getReservations() { return reservations; }

public BranchOffice getBranchOffice()
{
return office;
}

/**
* @param name the name to set
* @throws InvalidRequiredStringException
*/
private void setName(String name) throws InvalidRequiredStringException
{
if (name != null && !name.trim().isEmpty())
this.name = name;
else
throw new InvalidRequiredStringException(INVALID_NAME, name);
}

private void setBranchOffice(BranchOffice office)
{
if (office == null) {
throw new IllegalArgumentException(INVALID_OFFICE);
} else {
this.office = office;
}

}

/**
* @param type the type to set
* @throws InvalidRequiredResourceTypeException
*/
private void setType(ResourceType type)
throws InvalidRequiredResourceTypeException
{
if (type == null)
throw new InvalidRequiredResourceTypeException(INVALID_TYPE, type);
else
this.type = type;
}

/**
* Set the list of reservations to a given list.
*
* @param reservations
* | The list you want to set the reservations to.
*/
private void setReservations(Set<Reservation> reservations)
{
if (reservations != null) this.reservations = new HashSet<>(reservations);
else this.reservations = new HashSet<>();
}

/**
* Adds a given reservation to the list of reservations.
*
* @param reservation
* | The reservation you want to add to the reservations.
*/
private void addReservation(Reservation reservation)
{
this.reservations.add(reservation);
}

/**
* Checks if this resource conflicts with a given resource.
*
* @param resource
* The resource you want to check against.
* @return
* True if this resource conflicts with the given resource.
*/
public boolean conflictsWith(Resource resource)
{
if (getType().hasConflictWith(resource.getType())) return true;
else return false;
}

/**
* Checks if a resource if available for a given timespan
*
* @param timespan
* @return True if the timespans do not overlap.
*/
public boolean isAvailable(TimeSpan timespan)
{
if (reservations != null && !reservations.isEmpty())
{
for (Reservation reservation : reservations)
if (reservation.overlapsWith(timespan))
return false;
// TODO: checken of resource beschikbaar is binnen timespan (bv.
// datacenter enkel beschikbaar tussen 12:00 en 17:00
return true;
}
return true;
}


@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}

@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Resource other = (Resource) obj;
if (name == null)
{
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

public ResourceValue getValue()
{
return new ResourceValue(this);
}

@Override
public int compareTo(Resource o)
{
return this.getKey().compareTo(o.getKey());
}

public boolean isOfType(ResourceType other)
{
return getType().equals(other);
}

public void reserve(Reservation newReservation) throws InvalidReservationException
{
for(Reservation reservation : getReservations())
if(reservation.conflictsWith(newReservation))
throw new InvalidReservationException("Reservation conflicts with another reservation", newReservation);
addReservation(newReservation);
}

public boolean isOfSameType(Resource resource)
{
return isOfType(resource.getType());
}

public class ResourceValue
{
private final String name;
private final ResourceType type;

private ResourceValue(Resource resource)
{
this.name = resource.getName();
this.type = resource.getType();
}
/**
* @return the name
*/
public String getName() { return name; }
/**
* @return the type
*/
public ResourceType getType() { return type; }
}

public void deleteReservation(Reservation reservation)
{
getReservations().remove(reservation);
}
}

我已经复制了整个类,它看起来有点乱,但尝试查看类的底部,在那里你可以找到值类。我选择这门课是因为它是最小的一门课。在此示例中,值类不会复制所有字段,而仅复制 View 所需的字段。

我的问题是:“有没有更简单的方法来保持 View 和 Controller 之间的封装?”

最佳答案

It just doesn't feel right to kind of duplicate all these classes

当您将应用程序拆分为多个层时,最好使用这种“ObjectValue”也称为“ObjectDto,数据传输对象”[1]。过去没有模型对象的纯粹副本,您可以根据您想要执行的操作添加、删除、修改字段。

也就是说,您可以使用一些库将实体“映射”到 ObjectValue。例如,ModelMapper http://modelmapper.org/ .

PersonDto personDto = mapper.map(personModel, PersonDto.class);

编辑:[1] 根据评论,ValueObject 和 DTO 不是同一件事,即使主要原理保持不变。 IMO,这只是命名约定的问题。

DTOs are simple objects that should not contain any business logic that would require testing

关于java - View 和 Controller 之间的封装,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31428352/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com