房贷计算之访问者模式
wo:我要贷款100w买套房
icbc:利率4.9% 上浮25%,还贷款不?
卧槽,银行真是黑啊
wo:怎么贷款啊?有什么方式
icbc:我们有等额本金和等额本息两种方式?
wo:不是说还有公积金吗?
icbc: 公积金?NO,目前额度紧张,本金的不好批,建议您选本息的
卧槽心中一万个羊驼奔驰而过…………
贷款肯定想知道每个月换多少?下面我们就先学习访问者然后据此来完成贷款月供计算
访问者模式
定义(源于GoF《Design Pattern》):表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
涉及角色
1.Visitor 抽象访问者角色,为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
2.ConcreteVisitor.具体访问者角色,实现Visitor声明的接口。
3.Element 定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
4.ConcreteElement 具体元素,实现了抽象元素(Element)所定义的接受操作接口。
5.ObjectStructure 结构对象角色,这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。
特点
访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。
访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。
实例:
贷款计算
1 定义到款信息:总额 年限 利率 上浮或者打折
protocol LoadProtocol {
func accept(visiter m:MonthMoneyProtocal)
}
class Load : LoadProtocol{
var years:Int
var interestRate: Double
var up:Double
var loadTotal: Double
init(total: Double, years: Int, rate: Double, up:Double=Double(0.0)) {
self.loadTotal = total
self.years = years
self.interestRate = rate
self.up = up
}
func accept(visiter m: MonthMoneyProtocal) {
m.calculate(load: self)
}
}
extension Load{
//等额本金没有本金额度
var averageMonthDiv: Double{
return self.loadTotal / Double(years*12)
}
//实际月利率
var monthLevel: Double{
return self.interestRate * Double(1+up) / Double(12)
}
}
2 分别定义等额本金、等额本息计算通用部分
protocol MonthMoneyProtocal {
func calculate( load: Load)->Void
var monthMoneys:[MonthMoney] { get }
var totalInterest: Double { get }
}
2.1 等额本金定义
class AverageCapital: MonthMoneyProtocal{
var monthDiv: Double = 0.0
var totalInterest: Double = 0.0
var monthMoneys:[MonthMoney] = [MonthMoney]()
func calculate(load: Load) -> Void {
self.monthDiv = load.averageMonthDiv
for i in 0 ..< load.years*12 {
// 利息
let interest = (load.loadTotal - self.monthDiv*Double(i)) * load.monthLevel
self.totalInterest = self.totalInterest + interest
//每月总额
let monthTotal = self.monthDiv + interest
// 剩余
let nextLoad = load.loadTotal - self.monthDiv*Double(I+1)
totalInterest = totalInterest + interest
let monthMoney = MonthMoney(load: load.loadTotal, nextLoad:nextLoad, time: i, ben: load.averageMonthDiv , interest: interest)
monthMoneys.append( monthMoney)
}
}
}
2.2等额本息计算
class AverageCapitalPlusInterest: MonthMoneyProtocal{
var x: Double = Double(0.0)
var monthMoneys:[MonthMoney] = [MonthMoney]()
var totalInterest: Double = 0.0
func calculate(load: Load) {
x = Double(load.loadTotal * load.monthLevel * ((Double(1) + load.monthLevel).m(n: load.years*12)) / ((Double(1) + load.monthLevel).m(n: load.years*12) - 1))
for i in 0 ..< 30*12 {
//剩余欠款总额
let am = load.loadTotal * (Double(1) + load.monthLevel ).m(n: i) - self.x * ((Double(1)+load.monthLevel).m(n: i)-1)/load.monthLevel
// 本月利息
let interest = am*load.monthLevel
//计算付出利息
self.totalInterest = self.totalInterest + interest
let ben = x-am*load.monthLevel
let monthMoney = MonthMoney(load: load.loadTotal, nextLoad: am, time: i, ben: ben, interest: interest)
// print(monthMoney)
// print("第\(i/12+1)年\(i%12+1)月===还款\(x) 本金\(ben) 利息\(interest)")
monthMoneys.append( monthMoney)
}
}
}
每月贷款信息展示
struct MonthMoney {
var load: Double
var nextLoad: Double
var time: Int
var ben: Double
var interest: Double
}
extension MonthMoney: CustomDebugStringConvertible{
var debugDescription: String{
return "第\(time/12+1)年\(time%12+1)月===\(ben+interest) 本金\(ben) 利息\(interest) 剩余\(nextLoad)"
}
}
到此基本信息都有了,现在我们看看怎么玩
贷款100w 利率4.9% 30年,上浮25%
真是尼玛的黑心
let myLoad = Load(total: 1000000, years: 30, rate: 0.049,up:0.25)
我们先计算 大家大款通用的选择方式:等额本息
let avCP = AverageCapitalPlusInterest.init()
myLoad.accept(visiter: avCP)
等额本息
然后再计算等额本金
myLoad.accept(visiter: avC)
等额本金
看到了吧,卧槽,贷款买房全给银行打工啦
学完,吐槽完啦,我们下期的目标是使用策略模式来实现我们的贷款计算……