依赖关系的倒置
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
动机:
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需要的变化,这些对象经常面临剧烈的变化,但是他们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序 (使用这些对象的程序) ”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
意图:
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。
Prototype模式的几个要点:
l Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
l Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
l Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
StringTokenizer.cs代码示例:
namespace CsharpPats
{
//String Tokenizer class
public class StringTokenizer {
private string data, delimiter;
private string[] tokens;
private int index;
public StringTokenizer(string dataLine) {
init(dataLine, " ");
}
private void init(String dataLine, string delim) {
delimiter = delim;
data = dataLine;
tokens = data.Split (delimiter.ToCharArray() );
index = 0;
}
public StringTokenizer(string dataLine, string delim) {
init(dataLine, delim);
}
public bool hasMoreElements() {
return (index < (tokens.Length));
}
public string nextToken() {
return nextElement();
}
public string nextElement() {
string s = tokens[index++];
while((s.Length <=0) && (index<tokens.Length ))
s = tokens[index++];
return s;
}
}
}
csFile.cs代码示例:
using System.IO ;
namespace CsharpPats
{
/// <summary>
/// A simple file handlng class
/// </summary>
public class csFile
{
private string fileName;
StreamReader ts;
StreamWriter ws;
private bool opened, writeOpened;
//-----------
public csFile() {
init();
}
//-----------
private void init() {
opened = false;
writeOpened = false;
}
//-----------
public csFile(string file_name) {
fileName = file_name;
init();
}
//-----------
public bool OpenForRead(string file_name){
fileName = file_name;
try {
ts = new StreamReader (fileName);
opened=true;
}
catch(FileNotFoundException ) {
return false;
}
return true;
}
//-----------
public bool OpenForRead() {
return OpenForRead(fileName);
}
//-----------
public string readLine() {
return ts.ReadLine ();
}
//-----------
public void writeLine(string s) {
ws.WriteLine (s);
}
//-----------
public void close() {
if (opened)
ts.Close ();
if(writeOpened)
ws.Close();
}
//-----------
public bool OpenForWrite() {
return OpenForWrite(fileName);
}
//-----------
public bool OpenForWrite(string file_name) {
try{
ws = new StreamWriter (file_name);
fileName = file_name;
writeOpened = true;
return true;
}
catch(FileNotFoundException ) {
return false;
}
}
}
}
Swimmer.cs代码示例:
using CsharpPats;
namespace SimpleProto
{
/// <summary>
/// Summary description for Swimmer.
/// </summary>
public class Swimmer:IComparable {
private string name; //name
private string lname, frname;//split names
private int age; //age
private string club; //club initials
private float time; //time achieved
private bool female; //sex
//---------
public Swimmer(string line) {
StringTokenizer tok = new StringTokenizer(line,",");
splitName(tok);
age = Convert.ToInt32 (tok.nextToken());
club = tok.nextToken();
time = Convert.ToSingle (tok.nextToken());
string sx = tok.nextToken().ToUpper ();
female = sx.Equals ("F");
}
//---------
private void splitName(StringTokenizer tok) {
name = tok.nextToken();
int i = name.IndexOf (" ");
if(i >0 ) {
frname = name.Substring (0, i);
lname = name.Substring (i+1).Trim ();
}
}
//---------
public int CompareTo(object swo) {
Swimmer sw = (Swimmer)swo;
return lname.CompareTo (sw.getLName() );
}
//---------
public bool isFemale() {
return female;
}
//---------
public int getAge() {
return age;
}
//---------
public float getTime() {
return time;
}
//---------
public string getName() {
return name;
}
//---------
public string getClub() {
return club;
}
//---------
public string getLName() {
return lname;
}
}
}
SwimData.cs代码示例:
using System.Collections ;
using CsharpPats;
namespace SimpleProto
{
/// <summary>
/// Summary description for SwimData.
/// </summary>
public class SwimData:ICloneable {
protected ArrayList swdata;
private int index;
//-----
public SwimData() {
swdata = new ArrayList ();
}
//-----
public SwimData(ArrayList swd) {
swdata = swd;
index=0;
}
//-----
public int count() {
return swdata.Count ;
}
//-----
public SwimData(string filename) {
swdata = new ArrayList ();
csFile fl = new csFile(filename);
fl.OpenForRead ();
string s = fl.readLine ();
while(s != null) {
Swimmer sw = new Swimmer(s);
swdata.Add (sw);
s = fl.readLine ();
}
fl.close ();
}
//-----
public object Clone() {
SwimData newsd = new SwimData(swdata);
return newsd;
}
//-----
public void moveFirst() {
index = 0;
}
//-----
public bool hasMoreElements() {
return (index < swdata.Count-1 );
}
//-----
public void sort() {
//sort using IComparable interface of Swimmer
swdata.Sort (0,swdata.Count ,null);
}
//-----
public Swimmer getSwimmer() {
if(index < swdata.Count )
return (Swimmer)swdata[index++];
else
return null;
}
}
}
事件代码:
swdata = new SwimData ("swimmers.txt");
reload();
}
lsKids.Items.Clear ();
swdata.moveFirst ();
while (swdata.hasMoreElements() ) {
Swimmer sw = swdata.getSwimmer ();
lsKids.Items.Add (sw.getName() );
}
}
SwimData newSd = (SwimData)swdata.Clone ();
newSd.sort ();
while(newSd.hasMoreElements() ) {
Swimmer sw = (Swimmer)newSd.getSwimmer ();
lsNewKids.Items.Add (sw.getName() );
}
}
reload();
}
使用原型模式
SexSwimData.cs代码示例:
using System.Collections ;
namespace DeepSexClone
{
/// <summary>
/// Summary description for SexSwimData.
/// </summary>
public class SexSwimData:SwimData
{
public SexSwimData() {
}
public SexSwimData(string filename):base(filename){}
public SexSwimData(ArrayList ssd):base(ssd){}
public void sort(bool isFemale) {
ArrayList swd = new ArrayList();
for (int i = 0; i < swdata.Count ; i++) {
Swimmer sw =(Swimmer)swdata[i];
if (isFemale == sw.isFemale() ) {
swd.Add (sw);
}
}
swdata = swd;
}
}
}
Swimmer.cs代码示例:
using CsharpPats;
namespace DeepSexClone
{
/// <summary>
/// Summary description for Swimmer.
/// </summary>
public class Swimmer:IComparable {
private string name; //name
private string lname, frname;//split names
private int age; //age
private string club; //club initials
private float time; //time achieved
private bool female; //sex
//---------
public Swimmer(string line) {
StringTokenizer tok = new StringTokenizer(line,",");
splitName(tok);
age = Convert.ToInt32 (tok.nextToken());
club = tok.nextToken();
time = Convert.ToSingle (tok.nextToken());
string sx = tok.nextToken().ToUpper ().Trim ();
female = sx.Equals ("F");
}
//---------
private void splitName(StringTokenizer tok) {
name = tok.nextToken();
int i = name.IndexOf (" ");
if (i > 0 ) {
frname = name.Substring (0, i);
lname = name.Substring (i + 1).Trim ();
}
}
//---------
public int CompareTo(object swo) {
Swimmer sw = (Swimmer)swo;
return lname.CompareTo (sw.getLName() );
}
//---------
public bool isFemale() {
return female;
}
//---------
public int getAge() {
return age;
}
//---------
public float getTime() {
return time;
}
//---------
public string getName() {
return name;
}
//---------
public string getClub() {
return club;
}
//---------
public string getLName() {
return lname;
}
}
}
SwimData.cs代码示例:
using System.Collections ;
using CsharpPats;
namespace DeepSexClone
{
/// <summary>
/// Summary description for SwimData.
/// </summary>
public class SwimData {
protected ArrayList swdata;
private int index;
//-----
public SwimData() {
swdata = new ArrayList ();
}
//-----
public SwimData(ArrayList swd) {
swdata = swd;
index=0;
}
//-----
public void cloneMe(SwimData swdat) {
swdata = new ArrayList ();
ArrayList swd=swdat.getData ();
//copy in swimmer objects
for(int i=0; i < swd.Count ; i++)
swdata.Add (swd[i]);
}
//-----
public int count() {
return swdata.Count ;
}
//-----
public SwimData(string filename) {
swdata = new ArrayList ();
csFile fl = new csFile(filename);
fl.OpenForRead ();
string s = fl.readLine ();
while(s != null) {
Swimmer sw = new Swimmer(s);
swdata.Add (sw);
s = fl.readLine ();
}
fl.close ();
}
//-----
public ArrayList getData() {
return swdata;
}
//-----
public void moveFirst() {
index = 0;
}
//-----
public bool hasMoreElements() {
return (index < swdata.Count-1 );
}
//-----
public void sort() {
//sort using IComparable interface of Swimmer
swdata.Sort (0, swdata.Count , null);
}
//-----
public Swimmer getSwimmer() {
if(index < swdata.Count )
return (Swimmer)swdata[index++];
else
return null;
}
}
}
事件代码:
swdata = new SexSwimData ("swimmers.txt");
reload();
}
lsKids.Items.Clear ();
swdata.moveFirst ();
while (swdata.hasMoreElements() ) {
Swimmer sw = swdata.getSwimmer ();
lsKids.Items.Add (sw.getName() );
}
SexSwimData newSd = new SexSwimData ();
newSd.cloneMe (swdata);
newSd.sort (opFemale.Checked );
lsNewKids.Items.Clear() ;
while(newSd.hasMoreElements() ) {
Swimmer sw = (Swimmer)newSd.getSwimmer ();
lsNewKids.Items.Add (sw.getName() );
}
}
reload();
}
具有相同接口的不同类
AgeSwimData.cs代码示例:
using System.Collections ;
namespace DeepSexClone
{
/// <summary>
/// Summary description for SexSwimData.
/// </summary>
public class AgeSwimData:SwimData
{
ArrayList swd;
public AgeSwimData() {
swdata = new ArrayList ();
}
//------
public AgeSwimData(string filename):base(filename){}
public AgeSwimData(ArrayList ssd):base(ssd){}
//------
public override void cloneMe(SwimData swdat) {
swd = swdat.getData ();
}
//------
public override void sort() {
Swimmer[] sws = new Swimmer[swd.Count ];
//copy in swimmer objects
for(int i=0; i < swd.Count ; i++) {
sws[i] = (Swimmer)swd[i];
}
//sort into increasing order
for( int i=0; i< sws.Length ; i++) {
for (int j = i; j< sws.Length ; j++) {
if (sws[i].getAge ()>sws[j].getAge ()) {
Swimmer sw = sws[i];
sws[i]=sws[j];
sws[j]=sw;
}
}
}
int age = sws[0].getAge ();
int agecount = 0;
int k = 0;
swdata = new ArrayList ();
bool quit = false;
while( k < sws.Length && ! quit ) {
while(sws[k].getAge() ==age && ! quit) {
agecount++;
if(k < sws.Length -1)
k++;
else
quit= true;
}
//create a new Swimmer with a series of X's for a name
//for each new age
string name = "";
for(int j = 0; j < agecount; j++)
name +="X";
Swimmer sw = new Swimmer(age.ToString() + " " +
name + "," + age.ToString() + ",club,0,F");
swdata.Add (sw);
agecount = 0;
if(quit)
age = 0;
else
age = sws[k].getAge ();
}
}
}
}
用原型模式能根据需要克隆类,这样,在运行时就可以添加或删除类。根据程序运行情况,可以在运行时更改一个类的内部数据表示,也可以在运行时指定新对象而无需创建一个新类。
用C#实施原型模式的困难在于:如果类早已存在,则不能改变它们来增加需要的克隆方法。另外,间接引用其他类的列也不能被真正克隆。
拷贝原型类的想法意味着,你对类中的数据或方法有足够的访问权,这样在克隆之后可以修改它们。这可能需要向原型类中添加数据访问方法,这样在克隆了该类后,就能修改数据。