这是一个设计问题。由于一个好的和优雅的设计总是取决于你的问题域的很多方面,所以从你问题中的简化代码中不可能真正估计出最好的解决方案是什么。我将尝试向您展示一些选项,这些选项在每种情况下都解决了进行数据隐藏并避免嵌套函数或多次创建的函数的方法。
由于您要求隐藏数据并保持 getTempResult1 对 getTempResult2 以外的任何内容隐藏,我将假设这些函数中的每一个都相当复杂并且可能由不同的人编写,并且您希望保持内部隐藏。这保证了为每一个创建一个不同的类,而不仅仅是一个函数。
我将用更具体的示例替换您的示例代码,并使用适当的 OOP 方法来解决问题。假设您正在构建一个电子商务应用程序。 Level1 将是一个发票类,而 dep1-4 可能是:购买价格、利润率、折扣率、税率。我们将创建一个非常简单的应用程序,它将计算:purchase price - discount + profit + taxes = total price
我希望这足以忠实地解决您的问题,以便您可以欣赏使用的一些 OOP 技术(对于已完成的计算来说,它在结构上仍然过于矫枉过正,但它是 OOP 的一课,并且在出现问题时允许大量的可扩展性域将来会变得更加复杂,例如您要走向国际,并且必须计算不同国家/地区的税收等)。
我将使用 OoJs library 在 JavaScript 中进行正确的 OOP。请参阅下面的代码working on jsfiddle。
电子商务应用程序的想法灵感来自 Shalloway 和 Trott 的《设计模式解释》一书
总之你会发现这个解决方案:
- 隐藏实现细节
- 没有嵌套函数
- 每个函数只创建一次
- 具有可扩展性和可维护性,并且在发生变化时具有灵活性
要求
所以使用我们的类的代码如下所示:
// Create your namespace to avoid polluting global
//
var eComm = eComm || {}
// this will be some factory code which will return the needed objects, it won't actually use them.
// Normally this should be a class living in our eComm namespace
//
function factory( db, clientID )
{
// we would assume here that the hardcoded rates would be found in the database using the client id.
//
var discount = new eComm.Discount( 5 ) // in %
var profit = new eComm.Profit ( 20, discount ) // in %
var taxRate = new eComm.TaxRate ( 5 , profit ) // in %
// note that I use a simple aggragation approach, because I don't know the
// actual complexity of your problem domain. It makes this very simple ecommerce
// code not entirely ideal. If we would just perform a chain of operations on
// a number, other design patterns would be more suited, like a decorator.
// It is not appropriate to just pass taxRate to Invoice, because it is no different
// than profit or discount, it just comes later in a chain of calculations.
// I have taken this approach to show that it is possible to hide steps of the
// implementation down a hierarchy.
//
return new eComm.Invoice( taxRate )
}
// now when we will actually use it, it looks like this
// wrapped it in a function call because on global scope
// we would have to put this entirely at the bottom
// if you put all your code in classes you don't have this
// problem. They can occur in any order
//
function usage()
{
var invoice = factory( "database", 1654 /* the client id */ )
invoice.addPurchase( 1574 ) // price in some currency
invoice.addPurchase( 1200 ) // a second purchase
// in reality you would probably also pass an object representing an output
// device to Invoice (a printer, or a pdf generator, ...)
//
console.log( invoice.total() )
}
实际的类。它看起来很长,但那是因为他们做的越少,开销就越大(相对而言)。为了简洁起见,我省略了越来越多的代码,因为这些类看起来都非常相似。
;( function class_Invoice( namespace )
{
'use strict'; // recommended
if( namespace[ "Invoice" ] ) return // protect against double inclusions
namespace.Invoice = Invoice
var Static = OoJs.setupClass( namespace, "Invoice" )
// constructor
//
function Invoice( taxRate )
{
// should do validation as javascript is loosely typed
//
if( "TaxRate" !== OoJs.typeOf( taxRate ) )
;// throw an error
// Data members
//
this.taxRate = taxRate
this.totalPrice = 0
this.Protected( "taxRate", "totalPrice" ) // if you want them available to subclasses
var iFace = this.Public( total, addPurchase ) // make these methods public
return iFace
}
// all your method definitions go here
//
function addPurchase( price )
{
this.totalPrice += this.taxRate.calculate( price )
}
function total()
{
return this.totalPrice
}
})( eComm )
;( function class_TaxRate( namespace )
{
namespace.TaxRate = TaxRate
var Static = OoJs.setupClass( namespace, "TaxRate" )
// constructor
//
function TaxRate( rate, profit )
{
// do your validation on profit and rate as above
this.rate = rate
this.profit = profit
this.Protected( "profit" ) // if you want
return this.Public( calculate )
}
function calculate( price )
{
return this.profit.calculate( price ) * ( 1 + this.rate / 100 )
}
})( eComm )
;( function class_Profit( namespace )
{
namespace.Profit = Profit
var Static = OoJs.setupClass( namespace, "Profit" )
// constructor
//
function Profit( rate, discount )
{
this.rate = rate
this.discount = discount
return this.Public( calculate )
}
function calculate( price )
{
return this.discount.calculate( price ) * ( 1 + this.rate / 100 )
}
})( eComm )
;( function class_Discount( namespace )
{
namespace.Discount = Discount
var Static = OoJs.setupClass( namespace, "Discount" )
// constructor
//
function Discount( rate )
{
this.rate = rate
return this.Public( calculate )
}
function calculate( price )
{
return price - price * this.rate / 100
}
})( eComm )
usage()