组合优于继承和紧耦合。

3

我是一个完全的初学者,所以请原谅我的无知。 我创建了一个项目,在其中一些类中使用了组合。 在我的Cinema类中,我有一个Schedule对象。

public class Cinema {        

private String name;        //set via constructor
private int seatCount;  // set in constructor  
private int rowCount;   // set in constructor  
private int cleanUpTime;        //set via constructor 
private LocalTime openTime = LocalTime.of(9, 30);        
private LocalTime closeTime = LocalTime.of(23, 59);      
private LocalTime peakTime = LocalTime.of(16, 30);   
private int costPerHour;        //set via constructor
private Schedule schedule = new Schedule(this);

//Constructors, other methods....
}

一个排片表属于一个电影院,其中某些方法需要使用到电影院对象。没有电影院,就不存在排片表。

当我阅读面向对象编程时,我被引导相信我已经创建了一个与另一个类紧密耦合的类,这可能是不好的。

那么我该如何改进这个设计呢?

我有一些紧密耦合的类,例如Booking类和Customer类。Booking有一个Customer,而Customer包含他们所做的所有Bookings的列表。

我以为我正在使用组合,并且这会很好,但现在我感到困惑了,因为我已经阅读到了关于耦合的内容。

请帮助我理解。


我认为组合与继承以及紧密耦合都是主观看法,不值得过多担心。更好的做法是问自己:这段代码是否健壮且易于维护? - NickJ
有趣。它非常强大且易于维护,因为它是一个非常小的应用程序。但这是关于良好理解面向对象编程的作业,我担心我的紧密耦合的类会让我失去宝贵的分数。 - Urban Gemz
4个回答

3

必须存在一些耦合。电影院和时间表并不完全独立。

时间表属于电影院。

到目前为止,还不错。

它需要一个电影院对象来执行某些方法。

不对。时间表对象应该能够自行运作。

由于您没有提供任何代码,我将做出以下假设。

  • 电影院播放一个或多个电影。
  • 每部电影每天都有一个时间表,持续时间为电影的上映时间。

这是一个时间表类。

public class Schedule {
    private final Calendar showingTimestamp;

    public Schedule(Calendar showingTimestamp) {
        this.showingTimestamp = showingTimestamp;
    }

    public Calendar getShowingTimestamp() {
        return showingTimestamp;
    }

    public int getShowingWeekday() {
        return showingTimestamp.get(Calendar.DAY_OF_WEEK);
    }

}

Schedule类中唯一的字段包含放映日期和放映时间。我向您展示了如何使用Calendar方法获取工作日。

这是一个基础的电影类。

public class Movie {
    private final String name;

    private List<Schedule> showingList;

    public Movie(String name) {
        this.name = name;
        this.showingList = new ArrayList<>();
    }

    public void addShowing(Schedule schedule) {
        this.showingList.add(schedule);
    }

    public List<Schedule> getShowingList() {
        return Collections.unmodifiableList(showingList);
    }

    public String getName() {
        return name;
    }

}

电影类知道时间表类。时间表类不知道电影类。

最后,这是电影院类。

public class Cinema {

    private final String name;

    private List<Movie> currentMovieList;

    public Cinema(String name) {
        this.name = name;
        this.currentMovieList = new ArrayList<>();
    }

    public void addCurrentMovie0(Movie movie) {
        this.currentMovieList.add(movie);
    }

    public void removeMovie(Movie oldMovie) {
        for (int index = currentMovieList.size() - 1; index >= 0; index--) {
            Movie movie = currentMovieList.get(index);
            if (movie.getName().equals(oldMovie.getName())) {
                currentMovieList.remove(index);
            }
        }
    }

    public List<Movie> getCurrrentMovieList() {
        return Collections.unmodifiableList(currentMovieList);
    }

    public String getName() {
        return name;
    }

}

Cinema类知道Movie类,间接地也知道Schedule类。但是Movie类不知道Cinema类。

希望这对你有所帮助。


感谢您的回复。为了提供更多背景信息,我的Schedule类创建Shows(Shows是另一个类)。它将这些Shows添加到作为成员的arraylist中。这个列表是本周的演出(类似于您的currentMovieList);但是是Schedule持有这个列表而不是Cinema。Schedule的方法从其Cinema对象获取信息。例如,清理时间、关闭时间、电影院大小。这使得放映可以正确地安排在电影院进行(例如大型电影院需要更长时间来清洁)。 - Urban Gemz
@Urban Gemz:你过度耦合了模型。按照我所描述的方式创建模型可以将耦合降至最低。 - Gilbert Le Blanc

2

在一个电影院对象中拥有一个电影排期对象列表是可以的。我想这是一对多的关系。


这更像是一对一的关系。一个电影院有一个时间表,而一个时间表只属于一个电影院。 - Urban Gemz

1
当适合时,使用组合而非继承确实是一种好的设计实践。但是你的问题并不是关于这个的:我没有看到一个可行的基于继承的替代方案来形成你所创建的组合。
假设你正在尝试为一个可以区分IMAX电影院和其他电影院的系统设计类,具有一致的接口但不同的行为。你可以考虑创建一个名为ImaxCinemaCinema子类,并根据需要覆盖方法以自定义其行为:
class ImaxCinema extends Cinema {
    @Override
    int getScreenWidth() {
        // ...
    }
}

这是“继承”替代方案。
另一方面,您可以创建一个接口ProjectorType,并使用实现StandardImax来实现不同的行为。如果给Cinema类一个类型为ProjectorType的成员,则可以通过分配给该成员的对象的类来提供不同的行为:
class Cinema {
    ProjectorType projector;

    int getScreenWidth() {
        return projector.getScreenWidth();
    }
}

这是“组合”替代方案的常见形式。

你的情况与继承和组合无关,因为没有涉及行为的定制。


避免类之间的紧密耦合是一个独立的考虑因素,也是一个良好的设计原则。您的Cinema和Schedule类确实紧密耦合,这已经在Schedule构造函数需要Cinema参数的事实中表现出来。
然而,请考虑Collections类的迭代器。每个迭代器本质上都特定于特定的集合类,因为它必须导航该类的特殊内部数据结构以正确地执行其工作。因此,每个迭代器的类与相关的集合类紧密耦合,这是可以接受的。避免紧密耦合通常是一个很好的原则,并不意味着每个设计的质量与耦合程度呈反相关。
在您的情况下,我不太清楚您从Schedule类中获得了什么优势,也不知道为什么它需要与Cinema紧密耦合。可能可以通过将Cinema的成员移入Schedule类中,并通过调用适当的Schedule方法来让Cinema访问它们来打破这种耦合。或者,将Schedule合并到Cinema中而不是作为单独的类可能更有意义。如果以上两种方法都行不通,则可以考虑将Schedule作为Cinema的内部类。

非常感谢您的回复。我认为不需要继承,因为我只能确定“HAS-A”类型的关系,但是感谢您提供了一个在我的应用程序中可以使用继承的有效示例。我也对您使用内部类的想法很感兴趣。 Schedule类的主要目的是根据电影院特征(以及电影时长)正确计算演出的开始和结束时间的所有逻辑。例如,它将电影院大小(行和座位)传递给演出,以便演出的座位安排是正确的。 - Urban Gemz

0

良好的设计应该避免紧耦合。通过创建一个新接口,在这里可以使得日程更加可重用。在接口中放置你想从电影院使用的所有方法。IDE 的编译器会帮助你完成这一步,不要忘记在电影院中添加 @Overridable 标注,针对那些接口方法。

public class Cinema implements Schedulable {
    private final Schedule schedule = new Schedule(this);

public class Schedule {
    public void Schedule(Schedulable schedulable) { // Instead of Cinema

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接