征优化的设计思路:组合条件查询的内部实现(既简单又复杂的问题)

kingkee 2003-09-11 11:04:25
案例:
需要做一个搜索功能,假设共有7个条件,3个必选(对应相应值),其它4个可选(选中后再输入具体的搜索值)。

传统的思路是使用if 之类的语句多层判断,然后转向相应的具体实现。(实现函数体各不相同)。

要求:
聪明的你,能否给出一个更好的思路:判断方便,具体的函数尽可能复用。如可能的话,可扩展性也尽可能好。

如有确实可行的优秀方法(1-2名),每名再加200分。

谢谢大家!

...全文
65 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
crii 2003-09-15
  • 打赏
  • 举报
回复
检索操作,如果要执行速度快,可以采用按照使用频率排列的2叉树结构最快。树的每个节点都只有一个判断条件,这样就可以快速定位条件的内容了。

至于建树,可以凭简单估计,建立设计期静态树;也可以根据使用使用的统计值,建立动态树。
kingkee 2003-09-15
  • 打赏
  • 举报
回复
谢谢大家,特别是: ThinkX(思·秋天的树·求职中) --功力不凡哦。

这两天太忙,还加班。。。。 :(

我找时间仔细研究一下你给的思路及代码。分是没有问题的。

pcclever 2003-09-14
  • 打赏
  • 举报
回复
这贴方子好,我收藏,先!
freshman2003 2003-09-14
  • 打赏
  • 举报
回复
to ThinkX(思·秋天的树·求职中) :呵呵,把问题上升到理论高度,又从理论回到了实践,厉害,向你学习,你写的那些东东够我看一阵了。
freshman2003 2003-09-13
  • 打赏
  • 举报
回复
我明白楼主的意思,大家说的都是针对sql,楼主需要的是更一般的情况,比如有四组每组四个checkbox进行条件组合,一共就有356种情况,如果不能代码复用,那就太臃肿了。
zbc 2003-09-13
  • 打赏
  • 举报
回复
如果条件表达式中值1与值2的类型不相同,可使用
条件?AnsiString(值1):AnsiString(值2)来进行转换。
zbc 2003-09-13
  • 打赏
  • 举报
回复
对于这种问题。我觉得使用条件表达式是最好的选择。我一直都是这样用的。例子如下:
AnsiString sql
sql="select * from 表 where 前三个条件"
sql = sql + ckeckBox1->Checked?" and 表达式":""
sql = sql + ckeckBox2->Checked?" and 表达式":""
sql = sql + ckeckBox3->Checked?" and 表达式":""
sql = sql + ckeckBox4->Checked?" and 表达式":""
这样看起来简单多了
ThinkX 2003-09-13
  • 打赏
  • 举报
回复
/////////////////////////

//这是另外一种搜索行为,它就是没有行为,而将其简单的委托给content了
//在这种搜索中condition提供一个key,然后委托给Content通过这个Key进行搜索,
//然后也将搜索结果放入ResultSet
struct NoneAction
{
template <class ConditionT, class ContentT, class ResultSetT>
static void Search(ConditionT& condition,
ContentT& content,
ResultSetT& resultSet)
{
typedef typename ConditionT::KeyType KeyType;
KeyType key;
if (!condition.Key(key))
return;
content.Search(key, resultSet);
}
};

//这就是可以提供Key的Condition,
//KeyT类型就是Key的类型,在函数virtual bool Key(KeyT& key)中,
//参数key是out的,如果Key()成功的提供了一个Key,那么返回true,key被改写,
//否则,如果Key()提供Key失败,那么返回false
template <typename KeyT>
struct KeyCondition
{
typedef KeyT KeyType;
virtual bool Key(KeyT& key) = 0;
};

//这是在对数据库中表进行搜寻时使用的Condition,它可以提供一个sql语句形式的Key,
//用来查询表
class SqlQueryKeyCondition :
public Composite<std::string>,
public KeyCondition<std::string>
{
private:
std::string _tableName;
public:
//需要提供一个表名称
SqlQueryKeyCondition(const std::string& tableName) : _tableName(tableName) { }
virtual ~SqlQueryKeyCondition() { }

//生成Sql语句形式的Key
virtual bool Key(std::string& key)
{
key = "SELECT * FROM " + _tableName;
if (Count() > 0)
key += " WHERE";

Container::iterator iter = _elements.begin();
for ( ; iter != _elements.end(); ++iter)
key = key + " AND " + (*iter);
return true;
}
};

class ADOContent;

//ADO搜索表时提供的ResultSet,它配合NoneAction使用,
//它应该是一个TDataSet的适配器
class ADOResultSet
{
friend class ADOContent;
private:
//TDataSet* _dataSet;
};

//ADO搜索表时需要使用Content,它配合NoneAction使用,
//它应该是一个TADOQuery的适配器
class ADOContent
{
private:
//TADOQuery* _query;
public:
void Search(const std::string& sqlKey, ADOResultSet& resultSet)
{
//_query->SQL = sqlKey;
//_query->ExecSQL();
//resultSet._dataSet = _query;
}
};


/////////////////////////


template <class STLContainerT>
void Print(const STLContainerT& c)
{
typename STLContainerT::const_iterator iter = c.begin();
for ( ; iter != c.end(); ++iter)
std::cout << *iter << std::endl;
}

//////////////////////////

typedef std::vector<std::string> StringVector;
typedef std::list<std::string> StringList;

//在stl中枚举搜索的例子
//它将在一个vector<string>中进行枚举搜索,并将搜索结果保存到一个list<string>中
//搜索的条件是字符串长度>=3,字符串必须包含字母'h'
void EnumSearchDemo()
{
//要搜索的字符串列表
static const char* texts[] = { "this", "is",
"a", "enumerable", "search", "example" };

//在c中进行搜索,将结果保存到rs中
StringVector c(&texts[0], &texts[sizeof(texts) / sizeof(texts[0])]);
StringList rs;

std::cout << "要进行枚举搜索的vector:" << std::endl;
Print(c);

//利用STLContent对vector<string>进行适配,使其成为一个Content
STLContent<StringVector> content(c);
//利用STLResultSet对list<string>进行适配,使其成为一个ResultSet
STLResultSet<StringList> resultSet(rs);
//枚举搜索的行为
EnumerableAction action;

//这是检测字符串长度>=3的Condition
struct LengthGE_3 : public EnumElementCondition<std::string>
{
virtual bool Filter(const std::string& value)
{ return value.length() >= 3; }
};
//这是检测字符串包含字母'h'的Condition
struct ContainChar_H : public EnumElementCondition<std::string>
{
virtual bool Filter(const std::string& value)
{ return value.find('h') != std::string::npos; }
};

LengthGE_3 cond1; //条件1: 字符串长度>=3
ContainChar_H cond2; //条件2: 必须包含字符'h'

//生成一个复合(组合)条件,同时满足上面两个条件
CompositeEnumElementCondition<std::string> compositeCond;
compositeCond.Add(&cond1);
compositeCond.Add(&cond2);

//搜索,并将结果保存到rs中
Search(compositeCond, content, action, resultSet);

//打印搜索结果,其实只有"this"和"search"复合条件
std::cout << "下面是枚举搜索后的结果:" << std::endl;
Print(rs);
}

//只是NoneAction搜索的例子
void NoneActionSearchDemo()
{
//生成查询需要的Key
SqlQueryKeyCondition sqlQueryCond("产品");
sqlQueryCond.Add("单价数量 > 10");
sqlQueryCond.Add("库存量 > 20");

std::string key;
sqlQueryCond.Key(key);

std::cout << "搜索使用的SQL语句:" << std::endl;
std::cout << " " << key << std::endl;

ADOContent content; //构建搜索用的Content, 需要ADOQuery作为参数
ADOResultSet resultSet; //创建搜索用的结果集
NoneAction action; //空搜索的行为,其实是将其委托给Content

Search(sqlQueryCond, content, action, resultSet); //搜索

//检索ResultSet中的值
}

#pragma argsused
int main(int argc, char* argv[])
{
std::cout << "下面是枚举搜索的例子" << std::endl;
std::cout << "============================================" << std::endl;
EnumSearchDemo();

std::cout << std::endl << "下面是使用Key进行搜索的例子" << std::endl;
std::cout << "============================================" << std::endl;
NoneActionSearchDemo();

std::cout << std::endl << "按键结束..." << std::endl;
std::cin.get();
return 0;
}
ThinkX 2003-09-13
  • 打赏
  • 举报
回复

#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#pragma hdrstop


//搜索就是用指定的条件在某些内容中通过特定的行为进行查找而得到需要的结果集。
//四个要素: 条件,内容,行为,结果集。
// Condition : 搜索的条件
// Content : 搜索的内容
// Action : 搜索的行为
// ResultSet : 搜索后保存搜索结果的结果集

template <class ConditionT, class ContentT, class ActionT, class ResultSetT>
void Search(ConditionT& condition,
ContentT& content,
ActionT& action,
ResultSetT& resultSet)
{
//搜索是以行为为中心,所以将其委托给Action
action.Search(condition, content, resultSet);
}

////////////////////////////////////////////////////

// 这是枚举搜索行为,具体动作为:
// 枚举Content中的每一项,然后用Condition进行过滤,
// 如果符合条件就加入到ResultSet中
struct EnumerableAction
{
template <class ConditionT, class ContentT, class ResultSetT>
static void Search(ConditionT& condition,
ContentT& content,
ResultSetT& resultSet)
{
typedef typename ContentT::Enumeration EnumType;
typedef typename EnumType::Element ElemType;

resultSet.Clear(); //先清空ResultSet
EnumType& enu = content.GetEnumeration(); //得到Content中的枚举器
while (enu.HasNext()) //开始枚举Content中的每一个元素
{
ElemType& elem = enu.Current();
if (condition.Filter(elem)) //如果Content中的元素符合Condition的条件
resultSet.Append(elem); //就将其加入到ResultSet中
enu.Next();
}
}
};

//这个类可以将stl中的容器适配成可枚举的Content
template <class STLContainerT>
class STLContent
{
public:
//枚举器
class Enumeration
{
private:
friend class STLContent<STLContainerT>;
typedef typename STLContainerT::iterator IterType;

STLContainerT& _container;
IterType _iter;

Enumeration(STLContainerT& c) : _container(c)
{ _iter = _container.begin(); }

public:
typedef typename STLContainerT::value_type Element;

bool HasNext() const
{ return _iter != _container.end(); }

void Next()
{ ++_iter; }

Element& Current()
{ return *_iter;}
};

private:
Enumeration _enumeration;
public:
inline STLContent(STLContainerT& c) : _enumeration(c) { }
inline ~STLContent() { }

//返回枚举器
Enumeration& GetEnumeration()
{ return _enumeration; }
};

//这个类可以将stl容器适配成储存枚举搜索结果的ResultSet
template <class STLContainerT>
class STLResultSet
{
private:
typedef typename STLContainerT::value_type ElemType;
STLContainerT& _container;
public:
inline STLResultSet(STLContainerT& c) : _container(c) { }
inline ~STLResultSet() { }

//加入一个元素到搜索的ResultSet中
void Append(const ElemType& elem)
{ _container.push_back(elem); }

//清空
void Clear()
{ _container.clear(); }

inline STLContainerT& GetContainer()
{ return _container; }
};


//这是个辅助类,用于实现简单的复合结构,类型 T 就是复合中元素的类型,
//其实它就是vector<T>做了一些包装,不过它之中不允许有相同的值,
//所以也许使用set更合适些
template <typename T>
class Composite
{
protected:
typedef std::vector<T> Container;
Container _elements;
public:
Composite() { }
virtual ~Composite() { }
bool Contains(const T& value) const
{ return _elements.end() !=
std::find(_elements.begin(), _elements.end(), value); }
//加入元素
bool Add(const T& value)
{
if (Contains(value))
return false;
_elements.push_back(value);
}

//删除元素
bool Remove(const T& value)
{
typename Container::iterator iter =
std::find(_elements.begin(), _elements.end(), value);
if (iter == _elements.end())
return false;
_elements.erase(iter);
return true;
}

//检索元素
T& Get(unsigned int index)
{ return _elements[(size_t)index]; }

const T& Get(unsigned int index) const
{ return _elements[(size_t)index]; }

//清空
void Clear()
{ _elements.clear(); }

//得到元素数量
unsigned int Count() const
{ return _elements.size(); }
};

////////////////////////////////

//这个interface就是在枚举搜索中的Condition,
//virtual bool Filter(const T& value)中value参数就是在
//枚举搜索时需要过滤的每一个元素,如果返回true,说明这个元素符合条件,
//就将value加入到ResultSet中
template <typename T>
struct EnumElementCondition
{
virtual bool Filter(const T& value) = 0;
};


//这个类就是在枚举搜索时的复合Condition,
//它可以将许多conditon组合起来,
//这和设计模式中的Composite模式有些相像
template <typename T>
class CompositeEnumElementCondition :
public Composite<EnumElementCondition<T>* >,
public EnumElementCondition<T>
{
public:
CompositeEnumElementCondition() { }
virtual ~CompositeEnumElementCondition() { }

//只有其子conditon中的每个条件都满足,此函数才返回true
virtual bool Filter(const T& value)
{
typename Container::iterator iter =
_elements.begin();
for ( ; iter != _elements.end(); ++iter)
if (!((*iter)->Filter(value)))
return false;
return true;
}
};
ThinkX 2003-09-13
  • 打赏
  • 举报
回复
我昨天晚上看到这个帖子,感觉很有意思,
今天上午我仔细想想,写了一些关于这些的程序。
目的有两个: 1.尽量做的通用些 2.另外就是看看C++的表述能力有多强大。
所以程序挺难读。
可能有人认为它太复杂,没有必要,没关系,我只是玩玩而已。
希望有人认同其中的部分东西。

无论是在vector等stl容器中搜索,还是在数据库的表中搜索,或者是在文件夹中查找文件,都是搜索的一种。如果要通用,就是寻找它们之间的共同点。
我认为的搜索就是:
"搜索就是用指定的条件在某些内容中通过特定的行为进行查找而得到需要结果集。"
显然这之中有四个要素: 条件(Condition),内容(Content),行为(Action),结果集(ResultSet)。
其中又以行为作为中心。

所以我写了一个模板函数,其中有模板参数就是四个要素,
template <class ConditionT, class ContentT, class ActionT, class ResultSetT>
void Search(ConditionT& condition,
ContentT& content,
ActionT& action,
ResultSetT& resultSet)
{
//搜索是以行为为中心,所以将其委托给Action
action.Search(condition, content, resultSet);
}

搜索的行为就是搜索所用的方法,
一般有下面两种:
1.在线性容器中枚举其中每个元素,如果它符合要求的条件,就将其加入到结果集中。
2.没有搜索行为的行为,如在数据库表中搜索,我们不需要理会dbms如何完成搜索,只是提供一个conditon而已(其实这个conditon就是sql语句了),然后返回给我们搜索后的结果(就是RecordSet或者DataSet)。

当然还有许多的行为,例如在文件夹中搜索文件,搜索的算法就是一种搜索行为(Action)

行为是搜索的核心,因为即使其他两个要素(Condition,Content)都相同,如果行为不同,那么得到的结果(ResultSet)还是不同的。

下面我将我说的第一种在线性容器中枚举每个元素的行为(Action)用写成代码:
其中,我使用枚举器枚举内容(Content)中每个元素,枚举器由Content提供。
condition.Filter()判断枚举出来的每个元素是否符合条件,如果符合就将其加入到结果集中。

// 这是枚举搜索行为,具体动作为:
// 枚举Content中的每一项,然后用Condition进行过滤,
// 如果符合条件就加入到ResultSet中
struct EnumerableAction
{
template <class ConditionT, class ContentT, class ResultSetT>
static void Search(ConditionT& condition,
ContentT& content,
ResultSetT& resultSet)
{
typedef typename ContentT::Enumeration EnumType; //枚举器类型
typedef typename EnumType::Element ElemType; //元素类型

resultSet.Clear(); //先清空ResultSet
EnumType& enu = content.GetEnumeration(); //得到Content中的枚举器
while (enu.HasNext()) //开始枚举Content中的每一个元素
{
ElemType& elem = enu.Current();
if (condition.Filter(elem)) //如果Content中的元素符合Condition的条件
resultSet.Append(elem); //就将其加入到ResultSet中
enu.Next();
}
}
};

这种枚举搜索的行为最适合STL中的线性容器,如vector和list。
为了方便起见,我写了将stl容器适配成搜索内容和搜索结果集的两个类。
template <class STLContainerT> class STLContent;
template <class STLContainerT> class STLResultSet;
它们的模板参数都是一个stl容器类,如STLContent<vector<int> >和STLResultSet<list<int> >。
这样,就可以很方便的使用了:

===========================
typedef vector<string> StringVector;

StringVector c; //要搜索的内容
StringVector rs; //保存搜索结果的结果集

STLContent<StringVector> content(c); //进行Content适配
STLResultSet<StringVector> resultSet(rs); //进行ResultSet适配
EnumerableAction action; //枚举搜索行为
Condition conditon; //搜索的条件

Search(condition, content, action, resultSet); //进行枚举搜索

//这时rs中就是搜索后的结果了。
=============================

下面我说一下第二种搜索行为,那就是没有行为的搜索。
因为没有搜索行为,所以将它委托给Content进行。
它从Condition中获取一个搜索使用的Key,然后将其交给Content,用其搜索。

//这是另外一种搜索行为,它就是没有行为,而将其简单的委托给content了
//在这种搜索中condition提供一个key,然后委托给Content通过这个Key进行搜索,
//然后也将搜索结果放入ResultSet
struct NoneAction
{
template <class ConditionT, class ContentT, class ResultSetT>
static void Search(ConditionT& condition,
ContentT& content,
ResultSetT& resultSet)
{
//从Condition中得到搜索用的Key
typedef typename ConditionT::KeyType KeyType;
KeyType key;
if (!condition.Key(key))
return;
//用这个Key委托content进行搜索
content.Search(key, resultSet);
}
};

最后,我说一下搜索用的Condition,它是最灵活的东西。

首先,对于枚举搜索,我定义了一个接口:
template <typename T>
struct EnumElementCondition
{
virtual bool Filter(const T& value) = 0;
};
如果Filter()返回true,那么说明元素满足搜索的条件,这个元素就会被加入到ResultSet中。
其实这很简单,关键是组合搜索用的条件,使其完成复杂的搜索。

我想到的就是设计模式中的Composite模式,它可以产生组合的行为。
所以我定义了一个辅助类
template <typename T> class Composite
{
protected:
std::vector<T> _elements;
public:
bool Contains(const T& value) const; //是否包含value
bool Add(const T& value); //加入一个元素
bool Remove(const T& value); //删除一个元素
T& Get(unsigned int index); //得到某个元素
const T& Get(unsigned int index) const;
void Clear(); //清空
unsigned int Count() const; // 返回元素数量
};
可以看出它其实就是对vector做了些简单的封装而已,产生一个复合体。

下面是如何对Condition进行组合,
template <typename T>
class CompositeEnumElementCondition :
public Composite<EnumElementCondition<T>* >,
public EnumElementCondition<T>;
这样生成的类就可以对Condition进行组合了,很显然它也可以被另一个CompositeEnumElementCondition组合。

如果说要应用到实际当中的话,那么建立几个实用的conditon更好,
例如搜索的是一个字符串集合,如果选中了一个CheckBox,那么条件中就加入这个字符串的长度必须大于10。
class CheckBoxCondition : public EnumElementCondition<AnsiString>
{
private:
TCheckBox* _checkBox;
public:
CheckBoxCondition(TCheckBox* cb) : _checkBox(cb) { }
virtual ~CheckBoxCondition() { }
virtual bool Filter(const AnsiString& value)
{
if (_checkBox->Checked)
{
if (value.Length() > 10)
return true;
}
return false;
}
};
这样就完成了一个condition,当然这些conditon可以随意组合。

下面是完整的代码,我并没有将函数从类中分离出来,这样做其实不太好:
kingkee 2003-09-12
  • 打赏
  • 举报
回复
这只是针对数据库的查询而言。(那样实现起来当然简单)

如果我的数据是存在一个vector向量里呢?
supwjhuLoveCjj 2003-09-12
  • 打赏
  • 举报
回复
pengdali (大力 V3.0)
也许可以帮上你
Santos 2003-09-12
  • 打赏
  • 举报
回复
用CheckBox实现条件选择控制

if(CheckBox1->Selected)
...
Robin 2003-09-12
  • 打赏
  • 举报
回复
要具体考虑条件的形式!才能有最佳的答案!
如果条件是字符,逻辑,数字型的信息,可能最优的手段不同!

Robin 2003-09-12
  • 打赏
  • 举报
回复
逻辑不合理!
你怎么知道前三个已经选择了呢!
yydy 2003-09-11
  • 打赏
  • 举报
回复
关注
87607047 2003-09-11
  • 打赏
  • 举报
回复
同意楼上的!我感觉也只能这样了,学习!
52vc 2003-09-11
  • 打赏
  • 举报
回复
AnsiString sql
sql="select * from 表 where 前三个条件"
if(选择了第四个)
sql+=" and 表达式"
if(选择了第五个)
sql+=" and 表达式"
if(选择了第六个)
sql+=" and 表达式"
if(选择了第七个)
sql+=" and 表达式"
................
xiaozerong 2003-09-11
  • 打赏
  • 举报
回复
关注

13,825

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder相关内容讨论区
社区管理员
  • 基础类社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧