【问题标题】:Duck Typing: Translating Ruby Code into Javascript Utilizing Duck TypingDuck Typing:利用 Duck Typing 将 Ruby 代码翻译成 Javascript
【发布时间】:2014-11-29 02:14:58
【问题描述】:

这是《实用 Ruby 中的面向对象设计》一书中的一个示例。我有兴趣将这段 ruby​​ 代码翻译成 javascript,以便更好地理解 JS 中的鸭子类型。任何人都可以帮助翻译此代码以阐明如何最好地用 Javascript 编写此代码,或者至少帮助我入门吗?

这里的行程类充当其他类(Mechanic、Trip_Coordinator、Driver)的接口。其中 Trip 类中的 prepare 方法使用的是 Duck 类型的 preparer。

class Trip
    attr_reader :bicycles, :customers, :vehicle

    def prepare(preparers)
      preparers.each {|preparer|
         preparer.prepare_trip(self)}
    end
 end

class Mechanic
  def prepare_trip(trip)
    # Does something with trip.bicycles
  end
end


class Trip_Coordinator
  def prepare_trip(trip)
    # Does something with trip.customers
  end
end

class Driver
  def prepare_trip(trip)
    # Does something with trip.vehicle
  end
end

更新:

我添加了我认为可能是上述 Ruby 代码翻译的代码。但是,当我运行代码时,我得到以下输出和错误:

mountain
2
jeep

/home/ubuntu/user/tests/trip.js:3
       preparer.prepare_trip(this)

TypeError: Object [object Object] has no method 'prepare_trip'

建议或建议将不胜感激,以进一步提高对 JS 中鸭子类型的理解。

Javascript 代码:

var Trip = function(preparers){
    return preparers.forEach(function(preparer){
       preparer.prepare_trip(this)
    }
)};

var Mechanic = function(trip){
    // does something with trip.bicycles                                                                                                                                                           
    prepare_trip: console.log(trip.bicycles);
};

var TripCoordinator = function(trip){
    //does something with trip.customers                                                                                                                                                           
    prepare_trip: console.log(trip.customers);
};

var Driver = function(trip){
    //does something with trip.vehicle                                                                                                                                                             
    prepare_trip: console.log(trip.vehicle);
};

// customer wants to go on a trip for two and needs a car                                                                                                                                          
var planA = {
    bicycles: "mountain",
    customers: 2,
    vehicle: "jeep"
};

//customer wants to go a trip for one and only ride a bike                                                                                                                                         
var planB = {
    bicycles: "road",
    customers: 1
};

Trip([new Mechanic(planA), new TripCoordinator(planA), new Driver(planA)]);
Trip([new Mechanic(planB), new TripCoordinator(planB)]);

更新 2

根据下面 fgb 的解决方案和建议,我已将我的问题的最终解决方案放在下面。我添加了一个代理来消除调用者在创建旅行计划时必须知道他们需要什么准备的依赖性。

var Agent = function(plan){
    if("bicycles" && "customers" && "vehicle" in plan){
        Trip([new Mechanic(plan), new TripCoordinator(plan), new Driver(plan)]);
    }
    else if(!("vehicle" in plan) && "bicycles" && "customers" in plan){ //A driver is not needed                                                                                                                                        
        Trip([new Mechanic(plan), new TripCoordinator(plan)]);
    }
};

var Trip = function(preparers){
    return preparers.forEach(function(preparer){
       preparer.prepare_trip(this)
    }
)};
var Mechanic = function(trip){
    // does something with trip.bicycles                                                                                                                                                           
    this.prepare_trip = function() {
        console.log(trip.bicycles);
    }
};

var TripCoordinator = function(trip){
    //does something with trip.customers                                                                                                                                                           
    this.prepare_trip = function() {
        console.log(trip.customers);
    }
};

var Driver = function(trip){
    //does something with trip.vehicle                                                                                                                                                             
    this.prepare_trip = function() {
        console.log(trip.vehicle);
    }
};

// customer wants to go on a trip for two and needs a car                                                                                                                                          
var planA = {
    bicycles: "mountain",
    customers: 2,
    vehicle: "jeep"
};

//customer wants to go a trip for one and only ride a bike                                                                                                                                         
var planB = {
    bicycles: "road",
    customers: 1
};

Agent(planB);

相关SO讨论Example of Javascript Duck Typing?

【问题讨论】:

  • 您可以使用 Opal 编译器将其自动转换为 JavaScript:sitepoint.com/opal-ruby-browser-basics
  • @AndersonGreen:代码生成器将如何帮助他们理解 JavaScript 中的鸭子类型?
  • @muistooshort 如果这是一个学习练习,当然最好手动翻译。
  • 这里似乎缺少很多东西。您想知道的 DT 是否只是 prepare_trip 方法的存在?您是否尝试过用 JavaScript 编写此代码并遇到任何具体问题?
  • 我更新了 Mechanic 方法以添加缺失的末端。手动翻译比使用自动翻译器更有帮助。我还没有把这段代码翻译成 JS。我想知道的 Duck Type 是以后如果需要的话能够添加一个 perparer 的能力。这是文本中这个 ruby​​ 示例的主要原因。通过更大的抽象使您的代码能够适应未来的变化。在我在 JS 中学习了这个技术之后,我想把它应用到我自己的编程 JS 兴趣中。

标签: javascript ruby oop duck-typing


【解决方案1】:

代码几乎是正确的,但是您混淆了构造函数和对象字面量的语法。当你这样做时:

var Driver = function(trip){
    //does something with trip.vehicle                                                                                                                                                             
    prepare_trip: console.log(trip.vehicle);
};

“prepare_trip:”实际上是一个label,并没有定义对象的属性。

修复它的一种方法是:

var Driver = function(trip){
    //does something with trip.vehicle                                                                                                                                                             
    this.prepare_trip = function() {
        console.log(trip.vehicle);
    };
};

还要注意代码与 Ruby 代码不太一样:

preparer.prepare_trip(this);

这里this是全局对象而不是trip对象,方法中没有使用参数。在您的代码中,trip 参数改为在准备者的构造器中传递。

【讨论】:

  • 您认为我提供的答案是最有效的解决方案吗?每次我打电话给旅行计划时,程序员都必须知道它需要调用哪些准备者。您认为进一步重构此代码是否会更好,以便仅将计划传递给 Trip 并且 Trip 函数根据特定计划决定需要调用哪些准备者?再次感谢您的帮助。
  • 我会考虑的。这取决于你想要什么样的设计。如果trip函数可以决定调用哪些preparers而不需要调用者指定它们,那么这可以使调用代码更简单。
猜你喜欢
  • 1970-01-01
  • 2015-08-17
  • 1970-01-01
  • 2019-09-18
  • 2016-10-29
  • 2016-05-06
  • 1970-01-01
  • 1970-01-01
  • 2018-09-09
相关资源
最近更新 更多