【问题标题】:Is this bad OO programming? Passing this down through methods这是糟糕的面向对象编程吗?通过方法传递它
【发布时间】:2012-07-09 10:00:31
【问题描述】:

好的,假设我有一个类,X 和 X 是与其他对象有聚合关系的东西。让我们假设 X 是一个足球场。

X 是全班观众。但是,每个观众对特定活动的行为不同。而不是 IF 语句,我希望不同的行为出现在观察者类中,以便我可以使用动态绑定。

但是,问题在于观众的行为会影响“足球场”课程。所以我正在考虑将“this”从足球场类通过一个方法传递给观众类,以便观众类可以对足球场类做点什么?

public class SoccerStadium{
    SpecatorInterface s = new Spectator();

    public void SpectatorBehaviour(){
        s.doSomething(this);
    }

    public void doSomethingthingBySpecator(){
    }
}

public class Spectator implements SpecatorInterface{
    public void doSomething(SoccerStadium s){
        s.doSomethingthingBySpecator();
    }
}

我只想这样做,以便我可以使用动态绑定并更改 Specator.doSomething() 中的行为,以便我可以将许多不同类型的 SpectatorSuperClass 作为属性传递给 SoccerStadium,然后具有不同的行为。

编辑:如果我通过 Spectator 构造函数将 Stadium 的引用传递给 Specator,而不是传递 this,会怎样?

【问题讨论】:

  • 我相信纯粹主义者会因为紧密耦合而畏缩这种技术,但它似乎被广泛使用。 “更好”的方法可能是创建体育场实现的接口,该接口定义外部实体可以对其执行的操作,并接受体育场作为观众类中的接口类型。
  • 观众究竟是如何影响足球场的?在我看来,这是确定最佳关系类型的关键。
  • @Esteban,非常简单的逻辑,将属性设置为值。没有什么重的
  • @user997112:您已将此问题标记为c#java。它是哪一个?如果您使用 C#,则可以使用 events,这似乎非常适合您的用例。
  • 很抱歉,因为它们非常相似。这是给java的。有未标记的 c#

标签: java oop design-patterns aggregation


【解决方案1】:

这与其说是“糟糕的 oo 编程”,倒不如说是 coupled。传递this 指针本身并没有错,但它很快就会变得一团糟。如果没有更多信息,我们真的不能说更多。

【讨论】:

  • 我有点试图通过指针传递一个方法(就像你在 C++ 中那样),除了我通过体育场调用的 Spectator 接口使用动态绑定来作用于体育场。 ...我想不出任何其他方式.....
  • 只是想,如果我不传递“this”,而是通过 Spectator 构造函数将 Stadium 的引用传递给 Specator 会怎样?
  • @user997112 Spectator 是否经常需要对不同的Stadiums 采取行动?如果Spectator 绑定到单个Stadium,那将是一个更好的解决方案。
  • @cklab,是的,一个 Spectator 实例总是与一个 Stadium 实例相关联
【解决方案2】:

我认为使用 this 作为参数没有问题。尽管如此,我不喜欢在SoccerStadium 类中硬编码的new Spectator() 调用。我相信你应该有一个带有createSpectator 方法的工厂,它可以接收一个参数,指示你打算创建哪种类型的观众。

【讨论】:

  • 老实说,这只是我很快搞砸的事情——我可以通过构造函数将 Spectator 传递到 Stadium,或者,像我的编辑一样,我可以做相反的事情?
  • 关于我的评论的要点是,您的代码 new Spectator() 将 Stadium 与特定类型的 Spectator 紧密结合。我在想你可以使用工厂模式。尽管如此,我认为每个体育场可能会有多个观众,因此您可以在体育场中拥有一个 List(或另一个容器)和一个 add 方法,该方法将接收 SpectatorInterface 作为参数。之后,Stadium 可以调用 doSomething(this)。
【解决方案3】:

对我来说,这种双向循环关系是个坏消息。如果观众想去剧院怎么办?

我会通过让 Stadium 成为 Spectator 调度事件的订阅者来解耦这种关系。

public class SoccerStadium
{
    ISpectator s = new Spectator();
    public SoccerStadium()
    {
        s.DidSomething+=DoSomethingthingBySpecator;
    }
    public void SpectatorBehaviour()
    {
        s.DoSomething();
    }
    public void DoSomethingthingBySpecator(object sender,EventArgs e)
    {
        Console.WriteLine("spectator did something");
    }
}
public interface ISpectator
{
    event EventHandler DidSomething;
    void DoSomething();
}
public class Spectator:ISpectator
{
    public event EventHandler DidSomething;
    public void DoSomething()
    {
        var ev=DidSomething;
        if(ev!=null)
        {
            ev(this,EventArgs.Empty);
        }
    }
}

...因此,观众现在可以与任何感兴趣的人交流,但不需要知道任何事情。

【讨论】:

  • 查看我对不同观众“容器”的编辑。我倾向于构建东西,所以小部件不依赖于它们的容器。它对我很有帮助。
  • 能麻烦我求个代码示例(如果要编辑的地方很少就用我的)?
  • @user997112 完成(在 c# 中,因为这是原始标签所述)。您将不得不四处寻找 Java 等价物。 Java 中有更多样板,因为 IIRC 是通过接口实现的。
【解决方案4】:

正如人们所说,紧耦合和你正在做的事情绝对没有错。但是,如果您想稍微解耦,请使用经典的访问者模式。

public interface SpectatorVisitor {
  ...
  void visit(Spectator spectator);
}

public class Spectator {
  ...
  public void accept(SpectatorVisitor visitor) {
      visitor.visit(this);
  }
}

public class Stadium {

  ...
  spectator.accept(new StadiumSpectatorVisitor());
}

如果您需要,可以更改访问方法签名以接受某种状态对象。否则,您可以简单地在 Spectator 类上定义相关方法,并让访问者收集更改体育场所需的信息。

例如:

public class Spectator {
  private Team supports;

  public Team getSupports() {
      return supports;
  }

  public void accept(SpectatorVisitor visitor) {
      visitor.visit(this);
  }
}

public class SupportedTeamVisitor {
  private Map<Team, AtomicLong> supportCount = new HashMap<Team, AtomicLong>();

  public void visit(Spectator spectator) {
     Team supports = spectator.getSupports();
     if (! supportCount.contains(supports)) {
       supportCount.put(team, new AtomicLong(0));
     }
     supports.get(team).incrementAndGet();
  }

  public Map<Team, AtomicLong> getSupportCount() {
     return supportCount;
  }
}


public class Stadium {

  public long getSupportCount(Team team) {
     SupportTeamVisitor visitor = new SupportedTeamVisitor();
     for (Spectator spectator : spectators) {
        spectator.accept(visitor);
     }
     AtomicLong count = visitor.getSupportCount().get(team);
     return (count == null) ? 0 : count.get();
  }
}

有意义吗?

【讨论】:

    【解决方案5】:

    你的实现绝对没问题,我以前见过那种东西。是的,您可以通过 Spectator 构造函数传递 Stadium 引用,这可能比每次需要时都发送引用更干净。

    但是,我不太喜欢它;我更喜欢内部类。目前尚不完全清楚您要做什么,但可能有这样的事情:

    public class Outer {
    
    private int someVariable=0;
    
    public void someMethod(){
        ExtendsInner ei = new ExtendsInner();
        ei.innerMethod();
        System.out.println(someVariable);
    }
    
    private void anotherMethod(){
        someVariable++;
    }
    
    public abstract class Inner {
        public abstract void innerMethod();
    }
    
    public class ExtendsInner extends Inner{
        public void innerMethod(){
            anotherMethod();
            someVariable++;
        }
    }
    
    public static void main(String[] args){
        Outer o = new Outer();
        o.someMethod();
    }
    }
    

    不幸的是,您必须将所有“旁观者”类放在另一个类中,这可能会导致一个非常长的文件,从而产生丑陋的代码。

    但是,我认为你绝对应该避免同时做这两件事,因为它肯定会使你的代码过于复杂。

    【讨论】:

    • 对不起,你的权利,不是很清楚。我想说:不要使用内部类,然后在内部类中引用外部类。我以前见过这样做,我想不出你需要这样做的任何理由。
    【解决方案6】:

    正如马特所说,您所描述的是访问者模式。尽管如此,我不认为这是你最好的选择(正如 Falmarri 所说,这种设计往往是紧密耦合的,你最终会在你的业务对象中加入很多逻辑,破坏SoCSRP 等。 .)。 每个观众对特定活动的行为不同的事实并不意味着逻辑应该包含(也不通过)观众类。有很多不同的方法可以避免这些 IF 语句。我建议你使用像this linksuggest 这样的东西,它比 if 语句、访问者模式或所有其他替代方案更强大,而且在另一个类中实现它真的很容易,并维护所有这些商品 OOP 原则(这是有原因的)。

    【讨论】:

      猜你喜欢
      • 2015-09-02
      • 2017-07-05
      • 1970-01-01
      • 2015-07-23
      • 1970-01-01
      • 1970-01-01
      • 2015-03-13
      • 1970-01-01
      • 2012-09-06
      相关资源
      最近更新 更多