`
lovnet
  • 浏览: 6705002 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

(原创)无废话C#设计模式之十六:State

阅读更多

无废话C#设计模式之十六:State

意图

允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

场景

我们在制作一个网上书店的网站,用户在书店买了一定金额的书后可以升级为银会员、黄金会员,不同等级的会员购买书籍有不同的优惠。你可能会想到可以在User类的BuyBook方法中判断用户历史消费的金额来给用户不同的折扣,在GetUserLevel方法中根据用户历史消费的金额来输出用户的等级。带来的问题有三点:

l 不用等级的用户给予的优惠比率是经常发生变化的,一旦变化是不是就要修改User类呢?

l 网站在初期可能最高级别的用户是黄金会员,而随着用户消费金额的累计,我们可能要增加钻石、白金等会员类型,这些会员的折扣又是不同的,发生这样的变化是不是又要修改User类了呢?

l 抛开变化不说,User类承担了用户等级判断、购买折扣计算等复杂逻辑,复杂的User类代码的可维护性会不会很好呢?

由此引入State模式,通过将对象和对象的状态进行分离,把对象状态的转化以及由不同状态产生的行为交给具体的状态类去做,解决上述问题。

示例代码

using System;

using System.Collections.Generic;

using System.Text;

namespace StateExample

{

class Program

{

static void Main(string[] args)

{

User user = new User("zhuye");

user.BuyBook(2000);

user.BuyBook(2000);

user.BuyBook(2000);

user.BuyBook(2000);

}

}

class User

{

private UserLevel userLevel;

public UserLevel UserLevel

{

get { return userLevel; }

set { userLevel = value; }

}

private string userName;

private double paidMoney;

public double PaidMoney

{

get { return paidMoney; }

}

public User(string userName)

{

this.userName = userName;

this.paidMoney = 0;

this.UserLevel = new NormalUser(this);

}

public void BuyBook(double amount)

{

Console.WriteLine(string.Format("Hello {0}, You have paid ${1}, You Level is {2}.", userName, paidMoney, userLevel.GetType().Name));

double realamount = userLevel.CalcRealAmount(amount);

Console.WriteLine("You only paid $" + realamount + " for this book.");

paidMoney += realamount;

userLevel.StateCheck();

}

}

abstract class UserLevel

{

protected User user;

public UserLevel(User user)

{

this.user = user;

}

public abstract void StateCheck();

public abstract double CalcRealAmount(double amount);

}

class DiamondUser : UserLevel

{

public DiamondUser(User user)

: base(user) { }

public override double CalcRealAmount(double amount)

{

return amount * 0.7;

}

public override void StateCheck()

{

}

}

class GoldUser : UserLevel

{

public GoldUser(User user)

: base(user) { }

public override double CalcRealAmount(double amount)

{

return amount * 0.8;

}

public override void StateCheck()

{

if (user.PaidMoney > 5000)

user.UserLevel = new DiamondUser(user);

}

}

class SilverUser : UserLevel

{

public SilverUser(User user)

: base(user) { }

public override double CalcRealAmount(double amount)

{

return amount * 0.9;

}

public override void StateCheck()

{

if (user.PaidMoney > 2000)

user.UserLevel = new GoldUser(user);

}

}

class NormalUser : UserLevel

{

public NormalUser(User user)

: base(user) { }

public override double CalcRealAmount(double amount)

{

return amount * 0.95;

}

public override void StateCheck()

{

if (user.PaidMoney > 1000)

user.UserLevel = new SilverUser(user);

}

}

}

代码执行结果如下图:

代码说明

l User类型是环境角色。它的作用一是定义了客户端感兴趣的方法(购买书籍),二是拥有一个状态实例,它定义了用户的当前状态(普通会员、银会员、黄金会员还是钻石会员)。

l UserLevel类型是抽象状态角色。它的作用一是定义了和状态相关的行为的接口,二是拥有一个环境实例,用于在一定条件下修改环境角色的抽象状态。

l NormalUser SilverUserGoldUser以及DiamondUser就是具体状态了。它们都实现了抽象状态角色定义的接口。

l User转化UserLevel的操作是在各个具体状态中进行的。在这里可以看到这种状态的转化是有序列的,这样也只会有前后两个状态产生依赖。假设现在的会员系统中是没有钻石用户的,那么GoldUserStateCheck()方法中应该是没有什么代码的,即使以后再要加DiamondUser类,我们也只需要修改GoldUserSateCheck()方法,以根据一定的规则来为环境转化状态。

l 在这里,我们在环境中调用了StateCheck方法,在实际应用中,你可以根据需要在抽象状态中引入模版方法,对外公开这个模版方法,并且在模版方法中调用行为方法和转化状态的方法,当然,具体的行为还是由具体状态来实现的。

何时采用

l 从代码角度来说,如果一个类有多种状态,并且在类内部通过的条件语句判断的类状态来实现不同行为时候可以把这些行为单独封装为状态类。

l 从应用角度来说,如果一个对象有多种状态,如果希望把对象状态的转化以及由不同状态产生的行为交给具体的状态类去做,那么可以考虑状态模式。

实现要点

l 在环境角色中拥有状态角色的实例。

l 在状态角色中拥有环境角色的实例用于在具体状态中修改环境角色的状态。

lfon

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics