如何检查实例是否有跳过显示值的对象?

时间:2010-05-21 03:26:43

标签: ruby-on-rails ruby polymorphic-associations null

我围绕名为status的模型创建了一个多态关联。

某些联系人将拥有与之关联的状态。许多人不会。

如果我在没有状态时尝试调用状态,则会出现错误。现在,即使我没有为模型创建状态,它仍然可以运行if-end块中的任何内容。

这是我正在尝试的,但它不起作用:

<% if !@status.nil? %>
       <p>Status: <%= @status.find(:last).status %></p>
<% end %>

在控制器中,它定义如下:

@status = Contact.find(@contact).statuses

顺便说一句,也开放使代码更具可读性和干燥性。

4 个答案:

答案 0 :(得分:0)

让它干嘛:

@statuses = @contact.statuses

<% unless @statuses.nil? %>
       <p>Status: <%= @status.last.status %></p>
<% end %>

如果我理解您的代码以及您正在尝试正确执行的操作,这对您有帮助吗?

<% if @statuses.length > 0 %>
       <p>Status: <%= @status.last.status %></p>
<% end %>

答案 1 :(得分:0)

一种选择是不必检查是否存在状态,通过为所有联系人(创建联系人时)提供无效状态的默认状态。

你可以把它称为Null Object习惯用法:你在其他地方使用null(C,C ++,Java)或NULL(SQL)的对象 - 这实际上是Smalltalk用nil做的事情; nil是一个特殊对象,是UndefinedObject类的一个实例。

这个成语的优点是你不必检查“对象不存在”条件并进行特殊处理。这样可以实现更清晰的代码,并且更加面向对象,因为您的Null Object实例(而不是调用代码)可以确定调用其方法时要执行的操作。

这是一个Java示例,使用责任链模式。责任链基本上意味着拥有一个处理程序列表,每个处理程序都处理一些事情(命令)或将其传递给链中的下一个处理程序。我们将创建一个Null Object处理程序,当它被要求处理某些东西时它什么也不做。

首先,一般合同:

interface Handler {
  void handle( Command c ) ;
}

然后是Null Handler,带有(恐怖!)单身人士:

class NullHandler implements Handler {
  public static void NullHandler singleton = new NullHandler();

  void handle( Command c ) { /*no-op*/}
}

然后是每个其他类型的Handler的基类(这也使用了模板方法模式,这只是一个实现细节):

abstract class BaseHandler implements Handler {
  private Handler next;

  public BaseHandler( Handler next ) {
    this.next = next ;
  }

  public BaseHandler() {
    this( NullHandler.singleton ) ;
  }

  public void handle( Command c ) { 
    if( canHandle( c ) {
       doHandle( c ) ;
    } else {
       next.handle( c ) ;
    }
  }


  //Template Method hooks
  abstract boolean canHandle( Command c ) ;
  abstract void doHandle( Command c ) ;
}

所以我们得到的是有限的 - 我们可以next.handle( c ) ;而不是if next != null next.handle( c ) ;。但随着我们添加更多代码,价值变得更加明显。

假设我们要打印我们的责任链。一种方法是在代码中外化迭代链,每次测试以查看next == null。

但更好,更面向对象,我们可以让Chain自己做。再次,在BaseHandler的打印中,我们打印该实例的名称,然后调用next的打印方法。 NullHandler中的print方法不会打印任何内容,或者可能是“链末端”。

答案 2 :(得分:0)

我实际想出了这个:

<% unless @status.empty? %>
       <p>Status: <%= @status.find(:last).status %> <%= @status.find(:last).created_at.to_s(:long) %></p>
<% end %>   

答案 3 :(得分:0)

Contact.statuses返回数组的开放父级问题 - 总之。如果联系人没有状态,则数组为空([]) - 但不是nil。

我发现您现在正在使用empty?

我建议通过移动到模型来使其更加干燥。例如,采用这种简单的方法:

class Contact < ActiveRecord::Base
  ...
  def last_status
    @last_status ||= statuses.last
  end
end

使用此功能,您无需计算控制器上的@statuses变量。 它还会在statuses.last上缓存@last_status variable,因此只调用一次statuses.last。如果您不希望发生这种情况,请删除@last_status ||=部分。

然后您的观点可以按照您最初的预期或多或少地完成:

<% unless @contact.last_status.nil? %>
  <p>Status: <%= @contact.last_status.status %> <%= @contact.last_status.created_at.to_s(:long) %></p>
<% end %>