与容器操作的设计模式
与容器操作的设计模式
与容器操作的设计模式有Composite(结构型模式), Visitor(行为模式),Iterator(行为模式)
采用几种设计模式设计容器类对象,可大大减少暴露容器对象内部结构和状态的程序.从而降低代码的耦合程序,从而提高稳定性,降低客户使用这个类的复杂程度。
组合(Composite)模式
组合模式类图及意图见《23种模式目录》
组合模式的实例源于http://www-128.ibm.com/developerworks/cn/java/j-jinfh/ 在此基础上,我将其中的代码修改成c#.
现在需要 对一个软件产品管理系统的实体建模:某公司开发了一系列软件集(SoftwareSet),包含了多种品牌(Brand)的软件产品。每个品牌下面又有各种产品(Product)。
非面向对象的编程方式。
代码//File1.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class SoftwareComponent
{
public virtual void AddComponent(SoftwareComponent component)
{
throw new Exception("Can't add component");
}
}
class AbsSoftwareComposite : SoftwareComponent
{
public override void AddComponent(SoftwareComponent component)
{
if(component != null)
{
ComponentList.Add(component);
}
}
//为了演示方便,暴露内部成员
public List<SoftwareComponent> ComponentList = new List<SoftwareComponent>();
}
/// <summary>
/// 软件产品系列
/// </summary>
class SoftwareSet : AbsSoftwareComposite
{
}
class Brand : AbsSoftwareComposite
{
}
/// <summary>
/// 软件产品
/// </summary>
class Product : SoftwareComponent
{
public Product(double price)
{
_price = price;
}
public double getTotalPrice()
{
return _price;
}
protected double _price;
}
}
代码//Fil2.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
SoftwareSet softSet = new SoftwareSet();
softSet.AddComponent(new Product(9.8));
softSet.AddComponent(new Product(2.2));
double price = GetPrice(softSet);
Console.WriteLine(price);
Console.Read();
}
static double GetPrice(SoftwareComponent component)
{
//为了得到价格信息,暴露了SoftwareComponent类及子类的内部结构
//增加了代码的耦合度。这样的代码不易扩展。切记代码设计的核心
//原则:高聚敛,低耦合. 我们也可以看出,低耦合的前提之一是高聚敛
if (component is SoftwareSet)
{
List<SoftwareComponent> list = ((SoftwareSet)(component)).ComponentList;
double price = 0.0;
foreach (SoftwareComponent compt in list)
{
if (compt is Product)
{
price += ((Product)(compt)).getTotalPrice();
}
}
return price;
}
else if (component is Product)
{
return ((Product)component).getTotalPrice();
}
else
{
return 0.0;
}
}
}
}
“这段代码的好处是实现业务逻辑的时候无需对前面已经定好的数据结构做改动,并且效率比较高;缺点是代码凌乱而且频繁使用了类型转换,代码的可读性不强,如果层次多了代码就更加混乱。”
“面向对象的编程方式(将计算价格的方法加入数据结构中)下面我们采用面向对象的方式,可以这么做:在接口SoftWareComponent中加入一个方法,名叫getTotalPrice”
代码//File1.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class SoftwareComponent
{
public virtual void AddComponent(SoftwareComponent component)
{
throw new Exception("Can't add component");
}
public virtual double getTotalPrice()
{
return 0.0;
}
}
class AbsSoftwareComposite : SoftwareComponent
{
public override void AddComponent(SoftwareComponent component)
{
if(component != null)
{
ComponentList.Add(component);
}
}
public override double getTotalPrice()
{
double price = 0;
foreach (SoftwareComponent compt in ComponentList)
{
price += compt.getTotalPrice();
}
return price;
}
private List<SoftwareComponent> ComponentList = new List<SoftwareComponent>();
}
/// <summary>
/// 软件产品系列
/// </summary>
class SoftwareSet : AbsSoftwareComposite
{
}
class Brand : AbsSoftwareComposite
{
}
/// <summary>
/// 软件产品
/// </summary>
class Product : SoftwareComponent
{
public Product(double price)
{
_price = price;
}
public override double getTotalPrice()
{
return _price;
}
protected double _price;
}
}
///////////////////////////////////////////////////////////////////////////////////
//File2.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
SoftwareSet softSet = new SoftwareSet();
softSet.AddComponent(new Product(5));
softSet.AddComponent(new Product(8));
//如果softSet不是一个组合对象,也可以用getTotalPrice方法得到总价。
//就可以理解这句话"Composite使得用户对单个对象和组全对象具有一致性"
double price = softSet.getTotalPrice();
//对客户来说,他只关心price,不关心其内部是怎么样实现的。即尽量设计黑
//盒类,方便客户使用
Console.WriteLine(price);
Console.Read();
}
}
}
///////////////////////////////////////////////////////////////////////////////////
"现在把业务逻辑的实现 都放在了数据结构中(组合模式的结构中),好处很明显,每个类只管理自己相关的业务代码的实现,跟前面举的面向过程方式的实现方式相比,没有了强制类型转换。但是不好的地方是如果需要增加新的业务方法的话就很麻烦,必须在接口SoftWareComponent中首先声明 该方法,然后在各个子类中实现并且重新编译。"可以使用访问者模式解决上面遇到的问题。
访问者模式
访问者模式类图及意图见《23种模式目录》
面向对象分析设计方法并不适用于所有问题.有时,我们从面向数据结构的角度思考问题,问题会更清晰.我们分面向数据的角度分析Visitor模式.这种模式,将数据和对数据的处理分开.
Visitor访问的对象中储存了数据.Visitor子类定义了处理这些数据的方法.在设计时,尽量将不易变化的部分和易变化的部分分开.我们分面向数据的角度分析问题,数据是不易变化的,而数据处理方法很容易发现变化.所以,可以将数据结构和数据处理分开.例如,下面的代码, SoftwareComponent 及其子类,定义了数据结构, IVisitor及其子类定义了对这些数据结构中的数据的处理方法.分离了数据结构和数据处理方法.
"使用访问者模式就能解决上面提到的问题:如果要经常增加或者 删除业务功能方法的话,需要频繁地对程序进行重新实现和编译。根据面向对象设计原则之一的SRP(单一职责原则)原则,如果一个类承担了多于一个的职责, 那么引起该类变化的原因就会有多个,就会导致脆弱的设计,在发生变化时,原有的设计可能会遭到意想不到的破坏。下面我们引入了一个叫做Visitor的接 口,该接口中定义了针对各个子类的访问方法,如下所示:"
代码//File1.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
interface IVisitor
{
void VisitBrand(Brand brand);
void VisitSoftwareSet(SoftwareSet set);
void VisitProduct(Product product);
}
class CaculateTotalPriceVisitor : IVisitor
{
public CaculateTotalPriceVisitor()
{
price = 0;
}
public void VisitBrand(Brand brand)
{
}
public void VisitSoftwareSet(SoftwareSet set)
{
}
public void VisitProduct(Product product)
{
price += product.GetPrice();
}
public double getTotalPrice()
{
return price;
}
private double price;
}
class SizeCountVisitor : IVisitor
{
public void VisitBrand(Brand brand)
{
}
public void VisitSoftwareSet(SoftwareSet set)
{
size += set.Size * 100;
}
public void VisitProduct(Product product)
{
size += product.Size;
}
public double GetSize()
{
return size;
}
private double size;
}
class SoftwareComponent
{
public SoftwareComponent()
{
_price = 0;
_size = 0;
}
public virtual void AddComponent(SoftwareComponent component)
{
throw new Exception("Can't add component");
}
public virtual void accept(IVisitor visitor)
{
}
public double GetPrice()
{
return _price;
}
protected double _price;
protected double _size; //软件大小
public double Size
{
get
{
return _size;
}
set
{
_size = value;
}
}
}
class AbsSoftwareComposite : SoftwareComponent
{
public override void AddComponent(SoftwareComponent component)
{
if(component != null)
{
ComponentList.Add(component);
}
}
public override void accept(IVisitor visitor)
{
foreach (SoftwareComponent compt in ComponentList)
{
compt.accept(visitor);
}
//base.accept(visitor);
}
private List<SoftwareComponent> ComponentList = new List<SoftwareComponent>();
}
/// <summary>
/// 软件产品系列
/// </summary>
class SoftwareSet : AbsSoftwareComposite
{
public override void accept(IVisitor visitor)
{
visitor.VisitSoftwareSet(this);
base.accept(visitor);
}
}
class Brand : AbsSoftwareComposite
{
public override void accept(IVisitor visitor)
{
visitor.VisitBrand(this);
base.accept(visitor);
}
}
/// <summary>
/// 软件产品
/// </summary>
class Product : SoftwareComponent
{
public Product(double price)
{
_price = price;
}
public override void accept(IVisitor visitor)
{
visitor.VisitProduct(this);
}
}
}
///////////////////////////////////////////////////////////////////////////////////
//File2.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
SoftwareSet softSet = new SoftwareSet();
Product p1 = new Product(9);
Product p2 = new Product(10);
softSet.AddComponent(p1);
softSet.AddComponent(p2);
p1.Size = 5;
p2.Size = 2;
softSet.Size = 10;
CaculateTotalPriceVisitor priceVistor = new CaculateTotalPriceVisitor();
softSet.accept(priceVistor);
double price = priceVistor.getTotalPrice();
Console.WriteLine(price);
SizeCountVisitor sizeVisitor = new SizeCountVisitor();
softSet.accept(sizeVisitor);
double size = sizeVisitor.GetSize();
Console.WriteLine(size);
Console.Read();
}
}
}
还有一种容器操作模式: Iterator
Iterator模式
访问者模式类图及意图见《23种模式目录》
"在软件构建过程中,集合对象内部结构常常变化各异.但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种"透明遍历"也为"同一种算法在多种集合对象上进行操作"提供了可能.
使用面向对象技术将这种遍历机制抽象为"迭代器对象"为"应对变化中的集合对象"提供了一种优雅的方式." --- 李建忠《C#面向对象设计模式纵横谈》
下面的代码源于 李建忠《C#面向对象设计模式纵横谈》
Dot net 内容实现的迭代接口
代码//File1.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
//using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Patterns
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
// 迭代器的抽象接口
public interface IEnumerator
{
Object Current { get;} // 一个只读属性
bool MoveNext();
void Reset();
}
public class MyCollection : IEnumerable
{
int[] items;
public MyCollection()
{
items = new int[5] { 12, 44, 33, 2, 50};
}
public IEnumerator GetEnumerator()
{
return new MyEnumerator(this);
}
//在MyCollection内部,实现一个迭代器
public class MyEnumerator : IEnumerator
{
int nIndex = -1;
MyCollection collection;
public MyEnumerator(MyCollection coll)
{
collection = coll;
}
public bool MoveNext()
{
nIndex++;
return (nIndex < collection.items.GetLength(0));
}
public object Current
{
get
{
return (collection.items[nIndex]);
}
}
public void Reset()
{
nIndex = -1;
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////
//File2.cs
///////////////////////////////////////////////////////////////////////////////////
using System;
using System.Linq;
using System.Text;
namespace Patterns
{
class Program
{
static void Main(string[] args)
{
MyCollection col = new MyCollection();
//完全抽象于(就是不依赖于)集合结构的访问操作
IEnumerator ite = col.GetEnumerator();
//无论是哪种集合,只要实现了IEnumerator接口,
//下面的代码无须做任何改动,就可以访问任何
//集合中的元素.
while(ite.MoveNext())
{
int i = (int)ite.Current;
Console.WriteLine(i);
}
Console.Read();
}
}
}
///////////////////////////////////////////////////////////////////////////////////
pdf文档 http://download.csdn.net/source/2057835

浙公网安备 33010602011771号