我有一个包含几种方法的类,例如:
class mySqlTool{
private string _values, _table, _condition, _result;
public mySqlTool Select(string values = null){
//this is REQUIRED
_values = string.Format("select {0} ", values);
return this;
}
public mySqlTool Update(string table){
//this is REQUIRED
_table = table;
return this;
}
public mySqlTool Set(string name, String value){
//this is REQUIRED
//handle name and value
return this;
}
public mySqlTool From(string table = null){
//this is REQUIRED
_table = table;
return this;
}
public mySqlTool Where(string condition = null){
//this is OPTIONAL
_condition = condition;
return this;
}
public string Execute(){
//this is REQUIRED
//this is samplecode, of course here is checked if its select or update
//but to keep it short i erased it
statement = string.Format("{0} {1}", _values, _table);
if (!string.IsNullOrEmpty(_condition))
{
statement += string.Format(" where {0}", _condition);
}
//do some with statemen and fill result
return _result;
}
}
现在我以这种链式方式使用它:
MySqlTool t = new MySqlTool();
string result = t.Select("a,b,c").From("x").Where("foo=bar").Execute();
当我点击DOT (.)
时,我的VS为我提供了可用的方法。
我的问题是,我想在使用其他方法之前拒绝使用某些方法,例如:
MySqlTool.Where().Select().From().Execute();
在这种情况下,.C()
不应该被调用,因为.A()
被调用了。所以澄清什么是允许的,什么不是,这里有一个小清单
//Allowed
t.Select().From().Execute();
t.Select().From().Where().Execute();
t.Update().Set().Set().Set().Where().Where().Where().Execute();
//not Allowed
t.Select().Where().Execute();
t.Select().Select().Select().From().Execute();
t.From()...
t.Where()...
t.Execute()....
我读了一些有关接口和状态的内容,但我不确定这是否是我正在搜索的内容。
所以我的问题:
这是我想要的吗?
如果是,该技术如何调用?
答案 0 :(得分:11)
一般说明 - 请参阅结束以获取具体评论
这是我想要的吗?
不在同一个班级内,没有。编译器如何知道你已经调用了什么? (想象一下,你有一个类型为Test
的参数的方法 - 应该有哪些方法可以调用?)类型系统决定什么是有效的,什么不是 - 所以如果有不同的有效操作集,那表明不同类型。
所能做的是具有代表不同状态的不同类型,这些类型仅包含状态转换的适当方法。所以你可以这样:
class Test0 // Initial state
{
public Test1 A() { ... }
}
class Test1 // After calling A
{
public Test2 B() { ... }
}
class Test2 // After calling B
{
// This returns the same type, so you can call B multiple times
public Test2 B() { ... }
// This returns the same type, so you can call C multiple times
public Test2 C() { ... }
public string DoSomething() { ... }
}
然后你可以使用:
Test0 t = new Test0();
string x1 = t.A().B().DoSome();
string x2 = t.A().B().C().DoSome();
string x3 = t.A().B().B().B().C().C().C().DoSome();
...但是您的无效案例无法编译。
它有效,但它非常难看。在不知道方法要实现的目的的情况下,很难提出其他任何建议 - 但在许多情况下,使用可选参数的单个方法可能更好,或者可能是构建器模式。
另一种方法是使用单个类,而不是在编译时在执行时验证调用。这在编码时不太有用,但避免了大量的类型。
另一个替代方案是拥有一个类 - 并创建一个实例 - 但使用接口来表示状态。您的类将实现所有接口,因此它仍然可以返回this
:
interface IStart
{
IMiddle A();
}
interface IMiddle
{
IFinal B();
}
interface IFinal
{
IFinal B();
IFinal C();
string DoSomething();
}
class Test : IStart, IMiddle, IFinal
{
public IMiddle A(string x = null) { return this; }
public IFinal B(string x = null) { return this; }
public IFinal C(string x = null) { return this; }
public string DoSomethign { ... }
}
然后你就有了:
IStart t = new Test();
string x1 = t.A().B().DoSome();
string x2 = t.A().B().C().DoSome();
string x3 = t.A().B().B().B().C().C().C().DoSome();
但这对我来说感觉很不对劲。我希望A
,B
和C
方法能够以某种方式有效地改变状态 - 因此具有单独的类型将指示哪个状态可用。在第一个示例中,Test0
肯定不具有A
调用提供的状态,但Test1
确实 ... Test2
实例的状态由A
和B
提供,可能还有C
。
具体示例
对于给出的具体示例,我可能只是让构造函数处理 required 信息(表名)并使用属性/索引器来完成剩下的工作。我可能会从更新中分离出一个查询命令:
SqlQuery query = new SqlQuery("table")
{
Columns = { "a", "b", "c" },
Where = { "foo=bar" } // Not sure how you're parameterizing these
};
和
SqlUpdate update = new SqlUpdate("table")
{
// Which columns to update with which values
["a"] = 10,
["b"] = 20,
Where = { "foo=bar" } // Not sure how you're parameterizing these
};
在每种情况下都会有Execute
方法返回相应的结果。