[TL]{技术}在添加或者读写的时候,设计一个Helper类感觉很有趣。

113 views
Skip to first unread message

LeeoNix

unread,
Apr 15, 2009, 10:30:08 PM4/15/09
to TopLanguage
重载()操作符,将执行、添加、读写或者其他一类类型的方法,封装为一个类。
有的时候会得到别人能习惯使用,产生接口一致,又可以降低耦合度的好方式。
最近在写UI,所以就拿UI简单说一下。

class AddMemberHelper
{
public:
    AddMemberHelper();
    AddMemberHelper& operator()(const Button* value);
    AddMemberHelper& operator()(const Label* value);
    AddMemberHelper& operator()(const EditBox* value);
};

class Window
{
public:
    AddMemberHelper add_members()
    {
        return AddMemberHelper();  // 这里或许会可能传一些初始化的参数。
    }
};

int main()
{
    Window win;
    Button* btn = new Button;
    Label* lbl = new Label;
    EditBox* edt = new EditBox;
    win.add_members()
        ( btn )
        ( lbl )
        ( edt )
    ;
}

不知道大家的意见如何,我个人认为,这样设计,将接口方法独立为另一个类是很有意义的一个方法。
这样可以大大降低宿主类的接口变得特别繁复。特别这种作为“初始化”方式的功能,这个方法再适合不过了。
最近没怎么关注新技术,感觉这种设计会成为一种“流行”的趋势吧。

qiaojie

unread,
Apr 15, 2009, 10:47:37 PM4/15/09
to pon...@googlegroups.com
看不出跟
 win.add_members(btn);
 win.add_members(lbl);
 win.add_members(edt);
有什么差别
 

 
 win.add_members() ( btn ) ( lbl ) ( edt )
这种写法实在是丑陋,初次使用的人还得去研究AddMemberHelper是怎么工作的,无端的增加使用者的脑力负担。在接口上玩弄技巧是设计上的大忌。
 
 

 
2009/4/16 LeeoNix <leeo...@gmail.com>

chaoyan ma

unread,
Apr 15, 2009, 10:49:54 PM4/15/09
to pon...@googlegroups.com

?不是有泛型 模板之类的吗?

LeeoNix

unread,
Apr 15, 2009, 10:52:09 PM4/15/09
to pon...@googlegroups.com
你看不出就算了。呵呵。

2009/4/16 qiaojie <qia...@gmail.com>

LeeoNix

unread,
Apr 15, 2009, 11:02:52 PM4/15/09
to pon...@googlegroups.com
>看不出跟
> win.add_members(btn);
> win.add_members(lbl);
> win.add_members(edt);
>有什么差别

既然你看不出差别,所以咱们没有共同语言。

>win.add_members() ( btn ) ( lbl ) ( edt )
>这种写法实在是丑陋,

这种写法是我故意写的这样,我本来加了行注释://玩一下C++的小技巧。
不过后面黏贴的时候,好像是上个版本就发出去了。
为了省打字,也故意就是为了显示C++的方式。
没想到,第一个人,就喷了出来。
你可以这么写,只不过多了一个括号而已:
win.add_members() ( btn );
win.add_members() ( lbl );
win.add_members() ( edt );

>初次使用的人还得去研究AddMemberHelper是怎么工作的,无端的增加使用者的脑力负担。在接口上玩弄技巧是设计上的大忌。

难道不研究这个Helper类,初次使用者就不去研究Window类呢?按照你的逻辑,研究Helper类更容易研究一些,研究Window类岂不是更累?
没错,我就是玩弄C++技巧,既然C++支持这个玩意儿,和其他语言一样,那岂不是没意思?
如果你用这个,可以不重载操作符,也可以写个什么默认的add方法,重载了来用。

我说!!!!!上来就喷一口,连看都没看我表达的意思,你什么意思呢?

黑名单。

SpitFire

unread,
Apr 15, 2009, 11:05:36 PM4/15/09
to pon...@googlegroups.com
近来火气好象都挺大的,我比较愚钝,我也没看出你这样设计的意图?


2009/4/16 LeeoNix <leeo...@gmail.com>



--
SpitFire

rockeet

unread,
Apr 15, 2009, 11:11:55 PM4/15/09
to TopLanguage
这种写法是不是更好呢?把operator()变成operator<<就行了
win.add_members() << btn << lbl << edt;

进而你可以实现更复杂的逻辑,那个需要左对齐,哪个需要右对齐,哪个控件需要和其他的同时出现,哪个又和其它不能同时出现,哪个又包含了另外一些。
发挥你的想象力,使用一些类如 std::setw, std::left, std::right 的技巧,把界面的编写变成文本文件的输出一样简

LeeoNix

unread,
Apr 15, 2009, 11:23:57 PM4/15/09
to pon...@googlegroups.com
你误解我的意思了。
我得到认为<<这样的操作符有他具体的含义的。
而()操作符就是执行操作。所以还是重载它比较好。
<<和>>就是流入流出。从符号本身的含义就表达了。

而我这样做,就是做的像:
win.add_members(btn);
win.add_members(lbl);
win.add_members(edt);

对比一下:

win.add_members() ( btn );
win.add_members() ( lbl );
win.add_members() ( edt );

前一种,是把接口都设计到window类本身,

class Window
{
public:
    void add_members(const Button* value);
    void add_members(const Label* value);
    void add_members(const EditBox* value);

    AddMemberHelper add_members()
    {
        return AddMemberHelper();  // 这里或许会可能传一些初始化的参数。
    }
};

而后一种,Window类只是提供了一个Helper导出的接口,
就维护方面,单方面维护Helper,和统一维护Window,
我个人比较喜欢单方面维护某个Helper就像我例子里提到的。

而:

    win.add_members()
        ( btn )
        ( lbl )
        ( edt )
    ;

我是故意这么写的。

2009/4/16 rockeet <roc...@gmail.com>

qiaojie

unread,
Apr 15, 2009, 11:36:50 PM4/15/09
to pon...@googlegroups.com
哎..哎..哎....实在不应该参与到这种小白贴的讨论中....
 
class Window
{
public:
    void add_members(const Button* value);
    void add_members(const Label* value);
    void add_members(const EditBox* value);
};
 
从来没见过有这么傻X的接口设计。如果我有几十种控件,难道我要写几十个add_members函数?
难道你的Button,Label,EditBox没有公共基类的?写一个void add_members(Control* value); 不就结了么?
 
居然还加const修饰作茧自缚,哪天你的child对象里有一个parent变量,add_members的时候需要set_parent了怎么办呢?难道修改接口?要么内部const_cast一下?
 

 
2009/4/16 LeeoNix <leeo...@gmail.com>

LeeoNix

unread,
Apr 15, 2009, 11:50:36 PM4/15/09
to pon...@googlegroups.com
没有人强迫你参与到所谓的“小白”贴里面。

Ruby现在就在用这种方式。
CEGUI连一个基本的读写函数都这样做。
ORGE也在用这个。
OpenOffice的很多地方都在用这个方式。

嗯。我小白,其他成熟的软件和库设计的人一样小白。你是这个意思吧。呵呵。

2009/4/16 qiaojie <qia...@gmail.com>

up duan

unread,
Apr 15, 2009, 11:57:36 PM4/15/09
to pon...@googlegroups.com
讨论技术不可以吗?怎么都是只给观点只给感觉不给论据的呢?怎么就不能仔细看看别人的论据呢?怎么总是看到不同意见就“爆发”呢?

2009/4/16 LeeoNix <leeo...@gmail.com>

李强

unread,
Apr 15, 2009, 10:50:09 PM4/15/09
to pon...@googlegroups.com

重载()?不利于理解和使用吧,并也应用领域也受限制。
 
接口的作用是声明抽象的操作,如果需要实体的话应该直接用继承去实现。
 

 
2009/4/16 LeeoNix <leeo...@gmail.com>

Ian Yang, YIngcai

unread,
Apr 15, 2009, 11:08:01 PM4/15/09
to TopLanguage
Boost.Assign做了封装,make_list_inserter可以很容易的产生一个helper类

Ian Yang, YIngcai

unread,
Apr 15, 2009, 11:15:43 PM4/15/09
to TopLanguage
Boost.Assign做了这样的封装了,通过make_list_inserter能很容易的生成一个helper类

On Apr 16, 10:30 am, LeeoNix <leeoni...@gmail.com> wrote:

居振梁

unread,
Apr 16, 2009, 12:03:53 AM4/16/09
to pon...@googlegroups.com
是没什么语义上的区别,只是打字比较方便,就像>>操作符一样。

BTW,我也觉得重载<<更好看一点。
而且,对于我这种发散型(不确定这么形容是否合适)思维的人,将control添加到win里,解释为“输入进去”也没什么不可的。

2009/4/16 qiaojie <qia...@gmail.com>



--
自学走了不少弯路,更浪费了太多的时间,寻找良师益友。
追求黑客精神和清心寡欲的心态。
中文博客:http://wargrey.yo2.cn
英文博客:http://wargrey.blogspot.com
研究方向:基础[Unix/GNU Linux]、主观[人工智能]、客观[移动计算]、可选[虚拟化]
其他兴趣:数学、物理、心理学、武术、自然语言

LeeoNix

unread,
Apr 16, 2009, 12:05:14 AM4/16/09
to pon...@googlegroups.com
说老实话,用<<也不是不行。
仔细想想,用<<的感觉也不错哈。
也感觉控件向Window本身流入的一种感觉。
但就是不知道使用者是什么感觉。
他们第一眼是怎么理解的。

2009/4/16 rockeet <roc...@gmail.com>

LeeoNix

unread,
Apr 16, 2009, 12:14:05 AM4/16/09
to pon...@googlegroups.com
呵呵,我尝试做了一下修改,感觉还真不错哦。
其他地方的初始化也可以这么写,又不失去效率。
而且<<操作符重载可以写成全局的,不用写在类里面,让类接口感觉很清晰。

win.add_members() << btn << lbl << edt;
win << btn << lbl << edt;
后一种感觉是不是有点异样?
我还是感觉前一种有add_members说明会更清晰一些。

不过我怕的一点就是像Hub Scott分析sprintf的问题的时候所说的,
sprintf最大的优点就是用%d%s这些方便。

2009/4/16 rockeet <roc...@gmail.com>

LeeoNix

unread,
Apr 16, 2009, 12:22:27 AM4/16/09
to pon...@googlegroups.com
<<操作符重载确实适合我现在这种情况。
反复查看,比()操作符重载要有优势。
不过是适合传一个参数。

很有趣的是,
我把向脚本传参数的方式,简单修改为Helper类,
重载为<<输出,
感觉很有趣。
越看越有趣。
很像io的方式,
我第一眼感觉在UI上不太适合<<操作符,
但是在其他地方却很适合,看着也舒服。
哈哈,谢谢了啊。

2009/4/16 LeeoNix <leeo...@gmail.com>

LeeoNix

unread,
Apr 16, 2009, 12:31:57 AM4/16/09
to pon...@googlegroups.com
可以做到像iostream那样的功能,
又不至于把接口弄复杂。
提供单向的<<操作符,
又可以提供类似read和get之类的方法接口。
由Helper类扩展接口设计上。
还是有很多很有趣的事情啊。
回头仔细修改一下脚本接口方面的实现。

2009/4/16 LeeoNix <leeo...@gmail.com>

rockeet

unread,
Apr 16, 2009, 1:01:26 AM4/16/09
to TopLanguage
don't make operator<< as global, I was encountered this problem!
use a long name global function, let Class::operator<< call the global
function

On Apr 16, 12:14 pm, LeeoNix <leeoni...@gmail.com> wrote:
> 呵呵,我尝试做了一下修改,感觉还真不错哦。
> 其他地方的初始化也可以这么写,又不失去效率。
> 而且<<操作符重载可以写成全局的,不用写在类里面,让类接口感觉很清晰。''
>
> win.add_members() << btn << lbl << edt;
> win << btn << lbl << edt;
> 后一种感觉是不是有点异样?
> 我还是感觉前一种有add_members说明会更清晰一些。
>
> 不过我怕的一点就是像Hub Scott分析sprintf的问题的时候所说的,
> sprintf最大的优点就是用%d%s这些方便。
>

> 2009/4/16 rockeet <rock...@gmail.com>

LeeoNix

unread,
Apr 16, 2009, 1:19:12 AM4/16/09
to pon...@googlegroups.com
我还是倾向于全局的。
具体参照Hub Scott的Exceptional C++ Style
主要这样方便扩展。

2009/4/16 rockeet <roc...@gmail.com>

wing

unread,
Apr 16, 2009, 1:30:28 AM4/16/09
to pon...@googlegroups.com
在java或者动态语言例如ruby、python中,类似的设计很常见。不过LeeoNix举出的例子,因为我们不是特别了解应用场景,可能对大家理解问题本身反而产生了一些歧义,变成讨论助手类具体实现是否合理,显然这并不是一个关键问题,且要结合实际情况来讨论。

助手类将访问接口分离出来,在需要提供多种不同接口或接口有较多变动时,是有好处的,但增加了助手类,就增加了开销,这点在c++上是不能不考虑的,而java和ruby一般不考虑这微乎其微的损失,所有的灵活性都是有代价的,权衡只能看应用了。

btw:讨论问题要就事论事,大家火气小一点,否则就和论坛一样了,即便说一些小白的话,一笑而过好了,态度认真是好的,但应对事不对人。

--
wing
wing9...@gmail.com
Hope is a good thing, maybe the best of things.

LeeoNix

unread,
Apr 16, 2009, 3:01:17 AM4/16/09
to pon...@googlegroups.com
嗯,你说的有道理,动态语言常见。

我的工作有部分是和Lua互相交互,独立出助手类方面,在写交互的模块方面,很方便。

我们现在用不到那种很“巨大”的wrapper类。

开销方面。我倾向于初始之类的接口,用这个比较有利一点。

因为初始化的方法一般就执行一次,如果反复执行那就是方案本身就有问题一些。


2009/4/16 wing <wing9...@gmail.com>

LeeoNix

unread,
Apr 16, 2009, 3:17:04 AM4/16/09
to pon...@googlegroups.com
谢谢。我看了list_inserter的接口,也是重载的()执行。

貌似这个东东我没有接触过,去看看文档。

2009/4/16 Ian Yang, YIngcai <DoIT...@gmail.com>

OwnWaterloo

unread,
Apr 16, 2009, 8:36:54 AM4/16/09
to TopLanguage
这个设计的本质就在于:操作返回自身引用。
由此,操作就可以支持链式表达。

至于可以玩出什么花样, 就看需要了 ......


On 4月16日, 上午10时30分, LeeoNix <leeoni...@gmail.com> wrote:

raymond

unread,
Apr 17, 2009, 9:59:35 AM4/17/09
to TopLanguage
返回引用的确方便不少。我有一点不明白,如果在添加各种控件,比如button后还需要对button布局等其他进行操作,那么
AddMemberHelper这个类会不会膨胀起来?我觉得helper类应该短小精悍点,否则感觉感觉喧宾夺主了。各位能否讨论一下helper类
的设计原则或者经验,我的理解是不是就是帮助构建对象啊,读写小文件啊,固定形式的运算啊之类的?有些开源软件中还会专门有个utility的工程或者
类,这个utility是不是和helper类似?我没有实际经验,请大家帮忙解答一下。

On 4月16日, 上午10时30分, LeeoNix <leeoni...@gmail.com> wrote:

LeeoNix

unread,
Apr 18, 2009, 3:04:08 AM4/18/09
to pon...@googlegroups.com
用UI写的,我其实就是举个例子。我们不会这样做,因为UI的类型,我们会用注册机制,持久化提取去实现。

我只是在说一个方法。呵呵。

一般Helper类添加的,基本都是自己不修改的那种,所以由宿主去添加。Button的话,自然出现你所说的问题。

我换个例子你可能会明白,我向脚本里添加一个全局变量,比如就叫AddParamHelper。

class AddParamHelper
{
public:
        void push( bool value );
        void push( int value );
        void push( float value );
        void push( const char* value );
        void push( const std::string& value );
};

我想表达的大概就是这个意思,
看到这种样子你是否能理解Helper的意思呢?

BTW:
这里用了push,不敢用operator()了……被人看一眼就骂重载()操作符看着难看,然后用指点迷津的态度说……
我写个push还不是一样?只不过换个名字而已。

BTW again:
我打算模仿boost重载+=操作符和逗号操作符,
这样返回一个Helper类,
就可以这样做:Helper += 1, 2, 3, 4, 5;
这样是不是比<<更容易理解?

呵呵,小技巧让人一眼就能看懂逻辑是最好的了。

2009/4/17 raymond <shiq...@gmail.com>

Linker

unread,
Apr 18, 2009, 5:36:23 AM4/18/09
to pon...@googlegroups.com
这样好多了

--
Sent from my mobile device

Regards,
Linker Lin
linker...@gmail.com

Shuo Chen

unread,
Apr 18, 2009, 7:41:57 AM4/18/09
to TopLanguage
C++工具匮乏,在一个项目里,要找到一个函数的定义或许不算太难(最多就是分析一下重载和模板特化),但是要找到一个函数的使用就难多了。不比
Java,在Eclipse里Ctrl+Shift+G就能找到所有的引用点。
假如我要做一个重构,想先找到代码里所有用到add_members的地方,判断一下工作是否可行,基本上惟一的办法是grep。
用grep还不能排除同名的函数和注释里的内容。这也说明为什么要用 // 来引导注释,因为在grep的时候,一眼就能看出这行代码是在注释里的。

为什么C++要引入static_cast之类的转型操作符,原因之一就是像 (int*) pBuffer 这样的表达式基本上没办法用grep判断
出它是个强制类型转换,写不出一个刚好只匹配类型转换的正则表达式。
如果类型转换都用*_cast,那只要grep一下我就能知道代码里哪儿用了reinterpret_cast转换,便于迅速地检查有没有用错。

在我看来,重载operator()应仅限于和stl algorithm配合时使用,比如transform(),其他情况都用具名函数为宜。原因之
一是,我根本用grep找不到在哪儿用到了operator()。
例如Google Protocol Buffers的回调时class Closure,它的接口用的是virtual function Run
()而不是virtual operator()。
http://www.google.com/codesearch/p?hl=en#WTeibokF6gE/trunk/src/google/protobuf/stubs/common.h

On Apr 16, 10:47 am, qiaojie <qiao...@gmail.com> wrote:
> 看不出跟
> win.add_members(btn);
> win.add_members(lbl);
> win.add_members(edt);
> 有什么差别
>

> win.add_members() ( btn ) ( lbl ) ( edt )

> 这种写法实在是丑陋,初次使用的人还得去研究AddMemberHelper是怎么工作的,无端的增加使用者的脑力负担。在接口上玩弄技巧是设计上的大忌。
>
> 2009/4/16 LeeoNix <leeoni...@gmail.com>

LeeoNix

unread,
Apr 18, 2009, 10:27:54 AM4/18/09
to pon...@googlegroups.com
谢谢你解释。

你说的很有道理,重载operator(),确实很不利于他人阅读。
对于这种方式来说,只是确保与add_members一系列几口区别区别不大。

后面看到朋友介绍,可以用操作符连贯输出的方式,简洁明了。
所以已经开始改变这种想法了。

2009/4/18 Shuo Chen <gian...@gmail.com>

kusk

unread,
Apr 24, 2009, 12:20:49 AM4/24/09
to TopLanguage
传说中的C++的心智包袱。呵呵。无意冒犯。

个人感觉,如果是一些个人的,或者说小型的项目,这样的语法糖抽象的确是可以起到少敲代码、简洁直观的作用。
但假如是数万行、数十万甚至上百万行的大中型项目,里面可能包含了各个不同团队开发的库、模块的话,从维护角度,这样的"语言意外"用法还是少些为
妙。
这里的"语言意外"主要指的是过多地使用运算符重载。
因为在这样的项目里,每个开发人员读到的大部分code都不是他自己写的,有可能是其他人写的,也有可能是几年前离开团队的人写的。
所以,维护这样的代码,我宁愿看到100行老老实实的像
win.add_members(btn);
win.add_members(label);
...
这样的代码,而甚至不愿看到
win.add_members(btn)(label)(...);
或者(我认为更好一些的):
win << btn << label << ...;

因为前者虽然行数很多可能要写上百行,但都是机械性的,逻辑上非常简单。如果我要修改某个内容,search一下也不难。
对后者虽然代码数很少,但多了一层抽象逻辑,这意味着如果是debug跟踪到这段对我来说是新代码的情况的话,我还要找出这个运算符重载的定义,并且检
查这个抽象有没有问题。

而且数百万行的项目里不能指望所有代码的编写者都能沟通一致。可能在其它的库里,又会有这样的写法,于是在不同的小团队负责的不同模块里,我还可能分别
遇见:
networkStream < xxxData < yyyData << format.change(zzzFormat) <<
blablablabla....;
mainFramework += coolBtn += coolLabel += coolSubWindow -= toolBar;

当然上面还算小儿科,还有玩得更"专业"的团队写的:
dialog << (canvas = (OKButton("OK", Box(200, 300, 500, 700)) &=
pfnOnButtonOK &= OKNotificationFunction) && CancelButton("Cancel", Box
(200, 600, 500, 1000)) &= pfnOnButtonCancel)), text1(200, 300) % RGB
(255, 0, 0), text2(500, 200) * Mode(Text::Underline |
Text::TransparentBackground)

这里面用了各种操作符来"巧妙"地构建了一个对话框,包括创建各个控件,绑定相关的事件函数,设置控件格式。重载了包括"<<", "=",
"&=", ",", "%"...的操作符。
写的人是很爽,当年使用这一套机制的团队因为对这些东东熟练于心,用得也还算爽。但几年过去,作为新的维护者----可能是几年后的新人,或者是把这个模块
移植到另一个项目的人----看到这堆酷炫东东的时候,还要一个一个地方地去找这些符号的含义,复杂度不亚于新学习一套语法。这种情况下,至少我个人而言,
更宁愿阅读几十上百行的"愚蠢的"代码:
Text text1;
text.setPosition(200, 300);
text.setColor(255, 0, 0);
...
Box box(200, 300, 500, 700);
Button OKButton;
OKButton.setRect(box);
OKButton.addListener(pfnOnButtonOK);
OKButton.addListener(OKNotificationFunction);
...
好吧,虽然行数的确非常多,但形式上其实很整齐,都是机械重复,对于脑力上的理解,对Bug的定位、查找都很方便。单步调试的时候,后者是一行一行往下
走,打断点,观察都直观流畅,但对前者而言则是麻烦的事情。我也不需要去记不同模块、不同object可能会对"<<"有不同的重载这类容易引出混乱的
规则。甚至于像下面简单的例子:
window.addMember(button1);
很可能这是我跟踪到的某个从来没有打开过的源码文件中的某一行,但我可以信赖这是一个函数调用,而不用担心原作者重载了operator()然后背地里
做了什么我所不知道的东东于是保险起见还得查找window类的定义文件,然后做一个仔细search才可以确认。

像这样的语法糖抽象,其实节省的只是头一次编写时几分钟机械敲键盘的时间。但一个项目的进度瓶颈不在于这几分钟敲键盘的时间(如果是,只能说明你的项目
实在太简单了,不需要引入这么复杂的讨论来^_^)。等创建这套语法糖的开发v1.0的人们爽完了,在后面更长时间的维护、新版本创建期,这些东西带来
的将是噩梦:比如,新版本的开发由不同的新人来完成,或者,某个之后的新项目会引用的是不同团队编写的模块、库,然后不同的库都有各自一套的火星重
载、"仿函数潜规则",很明显,第一次编写代码省下来的机械几分钟完全无法弥补新时期去理解、维护、调试这些"语言之上的新语言"的机制的时间、脑力。
初看起来,几十上百行的setXXX, addXXX好像很重复很愚蠢,但正因为"愚蠢",才不至于成为理解上的困难。而且,这些"重复"是本质上的复
杂:你永远需要创建这么些不同的按钮,组件,定义不同的事件行为,这样的话,这些重复并不像是软件工程说的"避免代码重复"里的重复那样需要远避之。

当然,不能说绝对。像iostream那样的库也用了那样的重载。但有一个前提,你如果决定使用这套机制,你必须保证你的库的使用者(可能是整个企业的
人,甚至相关业界的人士)长期地统一坚持使用、熟悉这套东东,形成类似iostream那样雷打不动的标准,免去多次学习的负担。而且你一开始的设计必
须要足够正确,免得后面改了一个"%="操作符而千里之外的用户浑然不知。但如果你的库使用者来自外界,比如开源粉丝,他们愿不愿意学这一套那就不好说
了。比如可能开源界的另一人士又自己定义了一套火星重载库,于是大家都学不过来,转而使用老实的setButtonPos(...)库了。

因此,设计架构的时候,保持接口形式的简单一致是很重要的。最一致的东西是语言标准本身原生定义的像函数调用、内置操作符这样的东东。其实最朴素地使用
函数接口已经足够了。真正大的项目对C++这样有"扩展危险"的语言的使用是非常有节制的,比如限制运算符重载、限制template的使用等等。甚至
有当成"更好的C"来用的。这其间不无原因:大项目无一不需要长期的、多人的协作维护,各说各话是造不成通天塔的。

当然,如果你觉得你的设计只是用于学习C++,做做课程设计,只由不多于三个人维护(包括你的潜在代码观众),存活期不超过三年,那么以上的讨论可以作
废。毕竟自己搞自己的一套抽象,对于C++初学者而言还是很爽的体验,也应该鼓励----有多少人学C++的时候没有自己封过Array呢?:)

最后顺便说一下,其实你原来的需求不需要这样的流式重载也可以有很简洁明了的实现的:
1) 首先像这样的情况所有的Button, Label, EditBox实在是应该继承自一个统一的抽象接口,比如叫什么Control类。于是只
需要一个add_members(Control *pCtrl)
2) 然后剩下的代码就可以这样写:

Control *win_elements[] = { new Button, new Label, new EditBox, ...,
0 } // 空指针标记结尾,或者也可以记下数组的size
for (Control *pCtrl = &win_elements[0], *pCtrl, ++pCtrl)
{
win.add_members(pCtrl);
}

On Apr 16, 10:30 am, LeeoNix <leeoni...@gmail.com> wrote:

qiaojie

unread,
Apr 24, 2009, 12:42:43 AM4/24/09
to pon...@googlegroups.com
说的一点都没错。C++程序员只有真正认识到这一点,克服玩弄小聪明的心态,才能驾驭好C++,而不至于被语言本身的特性所打败。




2009/4/24 kusk <kuangs...@gmail.com>

sagasw

unread,
Apr 24, 2009, 12:50:42 AM4/24/09
to pon...@googlegroups.com
好像是云风提出一个观点,使用c的方式设计,使用c++编程。
c++到现在为止,开源lint工具几乎没有,代码静态检查手段也比较少,动态更别说了,
说白了就跟这些花招有很大的关系。
我个人在开发中坚持的一点就是:simple and stupid.

2009/4/24 kusk <kuangs...@gmail.com>

Eric.Wang

unread,
Apr 24, 2009, 1:04:37 AM4/24/09
to TopLanguage
说得非常好。没有做过大项目,没有碰过壁,是很难深刻体会到简单的好处的。简单不是代码少,而是易读易维护。

有些程序员可能会把自己写代码的机会当作试验田,试验各种他的新奇想法,这对公司是不负责任的,特别是当这个程序员还比较稚嫩或者品味不好的时候。而减
少这种情况的发生,则是技术经理的责任。

好的代码是看起来平平无奇,但是就是能很好的工作。说"返璞归真"是有点太高了,不过大概就是那个意思。

LeeoNix

unread,
Apr 24, 2009, 4:36:36 AM4/24/09
to pon...@googlegroups.com
你说的我都理解。你说的Control类是一种通用的方式而已,并不一定就是合适的。

很多网上有用的代码,也会写成我这样的类似AddButton的形式,比如:dxframework-1.0。

我前面已经多次提到,我举例子,只是拿UI举个例子而已,我个人根本就没这么写。

这样大家可以直观的表述Helper类的作用,再次重复,仅仅是例子。

而且我后面的例子也写了,我干脆用push去重载,省着某些人出来之后就喷操作符重载的事情。


class AddParamHelper
{
public:
        void push( bool value );
        void push( int value );
        void push( float value );
        void push( const char* value );
        void push( const std::string& value );
};

显然你写这些的时候,没有看到后面一系列的回答。

更为让人哭笑不得的是,标题是Helper类,你的回复中,一点都没有看到Helper类的讨论。

而是跑到了操作符重载的问题上了。我都说了,我用写push了还不行。

求您老人家了。

我真的怕了,大部分人都揪住一个点不放了。我说Helper类,随手写了两个地方,就被那么多人说。

2009/4/24 kusk <kuangs...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 4:46:58 AM4/24/09
to pon...@googlegroups.com
比如开发游戏。UI就是那几个东西。根本就不需要复杂的。

哪怕有个公共的Creator或者Factory去生成一些列的UI也无可厚非,也符合工厂模式的结构。

设计也是分形式的,不是一概而论的。当一个需求限定在某个圈子里,已经不会再有过多扩展的时候,你的话是否还继续合理呢?

后面有个朋友也提到了boost库里面的make_list_inserter

就可以清晰明了的表达一系列的操作,用重载+=操作符和,操作符。

用vec += 1,2,3,4,5,6;

你看这种方便?

vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
vec.push_back(6);

还是这样方便?

当我们大家都知道+=是个什么含义的时候,这样写是非常有意义的。

不是你所说的,我“宁愿”看多少多少行代码的那种。

我看了你写这么多,还是没怎么明白你说什么。

你是再说我写的例子代码里重载操作符的问题?还是AddMember的问题?

我后面的回复已经提到了,我改,我改还不行。

我承认我错了。我错了,大家就饶了我吧,例子代码也这样,我的标题都没人看了。

而我的标题是什么?Helper类而已,如果Helper类的设计有问题,请提出来。

我下次再也不敢写什么“花哨”的代码了。还有可能会出现的“不合理”的设计。

举例都这么麻烦,我实在受不了了。

2009/4/24 kusk <kuangs...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 4:49:42 AM4/24/09
to pon...@googlegroups.com
对了。你不是口口声声说什么“不想参加什么“小白”讨论吗”?

你怎么还进来了?我不是记仇。

你这种说话当废话的人,我实在不敢恭维。

2009/4/24 qiaojie <qia...@gmail.com>

Kenny Yuan

unread,
Apr 24, 2009, 4:58:32 AM4/24/09
to pon...@googlegroups.com
无责任提供例子一个

俺前些天做了不少这种事儿:

TODO:在一个timeline中进行坐标变换

item.setMatrix(Matrix().translate(offset_x, offset_y).rotate(axis_Y, 30).scale(0.5, 0.5));

当然,上面是我写的pseudo-code,为了没有背景知识的人也能看明白;熟悉cute的人应该知道可编译的书写形式


qiaojie

unread,
Apr 24, 2009, 5:01:26 AM4/24/09
to pon...@googlegroups.com
那你觉得这样写是不是可读性更高一些?
item.setMatrix(Matrix::translate(offset_x, offset_y) * Matrix::rotate(axis_Y, 30) * Matrix::scale(0.5, 0.5));



2009/4/24 Kenny Yuan <yuank...@gmail.com>

Kenny Yuan

unread,
Apr 24, 2009, 5:09:19 AM4/24/09
to pon...@googlegroups.com
就是由具体类库规定了使用方法的一个例子,都说了是无责任提供了,呵呵

2009/4/24 qiaojie <qia...@gmail.com>



--
Kenny Yuan
C++, UI, LISP, MMA, Psychology and Automobile.
BLOG: CS巴别塔(Computer Science Babel)
URL: http://blog.csdn.net/yuankaining/


qiaojie

unread,
Apr 24, 2009, 5:43:25 AM4/24/09
to pon...@googlegroups.com
我只是让你比较一下两种写法的可读性,我让你负什么责任了?真是太奇怪了。都这样的话我看也不用讨论了,大家尽管各说各话吧,反正无责任嘛,随便怎么说都是对的。

为什么我说Matrix().translate(offset_x, offset_y)可读性不好,因为光从调用上看不出translate(offset_x, offset_y)是把tanslate变换乘到原来的矩阵上还是重新设置矩阵为tanslate变换,如果是乘的话是左乘还是右乘也是看不出来的。


2009/4/24 Kenny Yuan <yuank...@gmail.com>

hyifeng

unread,
Apr 24, 2009, 6:13:18 AM4/24/09
to TopLanguage
大哥,您能不能把持一下自已的情绪?您的争吵实在太噪声了

Kenny Yuan

unread,
Apr 24, 2009, 6:16:45 AM4/24/09
to pon...@googlegroups.com
无责任提供例子,就是只提供例子,没有其它任何的责任,包括但不限于“解读”、“解释”或者“比较”等等,这样说OK了不?

不掺和这种话题就是为了防止某些事情的

炖鸡腿去也!



2009/4/24 qiaojie <qia...@gmail.com>

kusk

unread,
Apr 24, 2009, 7:20:06 AM4/24/09
to TopLanguage
好,我之前回复的是你直接的第一问题。现在回复你后面举的例子。
--------------------------------------------------
首先,出现像

vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
vec.push_back(6);

这样的代码,首先第一反应要考虑是不是设计上出问题了。我不认为增加运算符重载,或者其它方案这样的语法糖衣就能改进多少(相反,副作用可能更大)。
比如,在这个pushback的例子里,如果数据之间有内在逻辑的关联性的话,首先考虑能不能由程序生成这些数据。如果可以,初始化的时候for一下由
CPU生成当然是最简便的。

假如不能(或者代价太大),说明这些数据很可能(如果不是一定的话)是资源性质数据,而非引导程序流程的普通变量。这样一来,为什么还要把这么些数据硬
编码到代码段中,而不是分离开来?----比如说放到单独的数据资源段或者资源文件中然后由程序读入?要知道,资源数据硬编码到代码段中是设计上的大忌。应
当避免。

On 4月24日, 下午4时46分, LeeoNix <leeoni...@gmail.com> wrote:
> 比如开发游戏。UI就是那几个东西。根本就不需要复杂的。
>
> 哪怕有个公共的Creator或者Factory去生成一些列的UI也无可厚非,也符合工厂模式的结构。
>
> 设计也是分形式的,不是一概而论的。当一个需求限定在某个圈子里,已经不会再有过多扩展的时候,你的话是否还继续合理呢?
>
> 后面有个朋友也提到了boost库里面的make_list_inserter
>
> 就可以清晰明了的表达一系列的操作,用重载+=操作符和,操作符。
>
> 用vec += 1,2,3,4,5,6;
>
> 你看这种方便?
>
> vec.push_back(1);
> vec.push_back(2);
> vec.push_back(3);
> vec.push_back(4);
> vec.push_back(5);
> vec.push_back(6);
>
> 还是这样方便?
>
> 当我们大家都知道+=是个什么含义的时候,这样写是非常有意义的。
>
> 不是你所说的,我"宁愿"看多少多少行代码的那种。
>
> 我看了你写这么多,还是没怎么明白你说什么。
>
> 你是再说我写的例子代码里重载操作符的问题?还是AddMember的问题?
>
> 我后面的回复已经提到了,我改,我改还不行。
>
> 我承认我错了。我错了,大家就饶了我吧,例子代码也这样,我的标题都没人看了。
>
> 而我的标题是什么?Helper类而已,如果Helper类的设计有问题,请提出来。
>
> 我下次再也不敢写什么"花哨"的代码了。还有可能会出现的"不合理"的设计。
>
> 举例都这么麻烦,我实在受不了了。
>

> 2009/4/24 kusk <kuangsheng...@gmail.com>

> ...
>
> 阅读更多 >>- 隐藏被引用文字 -
>
> - 显示引用的文字 -

K.L.

unread,
Apr 24, 2009, 7:25:34 AM4/24/09
to TopLanguage
我不同意C++心智包袱说。boost::assign就是类似的东西,特定的情况下还是有用的。

kusk

unread,
Apr 24, 2009, 7:48:31 AM4/24/09
to TopLanguage
嗯,我也不百分百同意包袱说法。只是引用一个比喻。我也完全赞同boost::assign在特定情况下是有用的。当然,特定到什么程度,这个是可以讨
论的。:)

On Apr 24, 7:25 pm, "K.L." <xxxK.L....@gmail.com> wrote:
> 我不同意C++心智包袱说。boost::assign就是类似的东西,特定的情况下还是有用的。

chaoyan ma

unread,
Apr 24, 2009, 7:57:50 AM4/24/09
to pon...@googlegroups.com
我觉得 如果因为需要对某个方法连续操作 而重载  vec += 1,2,3,4,5,6; 这样的方式
连续add的数据来源如何?一般都是一堆数据同时来的时候吧?如果是单个来的 就不会出现
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
vec.push_back(6);
这样的情况了
而 一堆数据来的方式 如果是数组 list等等 倒不如重载
add(List list) add(T*a)这样的方法比较好
最终还是看为什么样的应用 什么样的数据来源提供这些接口吧 ~
2009/4/24 LeeoNix <leeo...@gmail.com>



--
最初的梦想

LeeoNix

unread,
Apr 24, 2009, 9:30:58 AM4/24/09
to pon...@googlegroups.com
我不想回答你了,这个事最后一次。唉,你拿我的例子不当例子看,你总是把问题集中在例子本身上,
我举得例子只是说了一种方式而已。最后我再举个例子,最后的了。
没有人集中在Helper讨论上,已经失去了这个帖的意义了。

打个比方。我要在鼠标移动的时候去调用一个Lua的函数,
我已经在内部规定了一系列的Lua需要的参数,有可能是一个或者多个。

lua的结构是这样的lua_push之类的。

比如这样的一个lua函数原型,
function mouse_move(sender, x, y)

end

我调用这个函数的时候需要操作。

lua_State* L = luaL_newstate();
lua_pushuserdata(L, this);
lua_pushnumber(L, x);
lua_pushnumber(L, y);

然后你会发现连续的push,这个是C语言的实现。

如果我有操作符重载,然后根据这个设计一个Helper类。

具体得到的结果大概就是:

luaHelper helper += this, x, y;

写lua的函数参数,最容易让人迷惑的就是,先入栈的是第一个参数。
不过还是会有人写成类似这样:
lua_pushuserdata(L, y);
lua_pushnumber(L, x);
lua_pushnumber(L, this);
这个人把这样的当成“栈”了。
有时候会让用的人误解,lua读取的参数有时候是错的。
而这样平行的样子就非常容易就和lua函数相匹配,

而看到this,x,y,就很容易得到lua所得到参数sender, x, y。

据此再次扩展:
如果需要执行mouse_down又会有mouse_button之类的参数。
如果是mouse_wheel之类的,又会有wheel_dalta之类的参数。

luaHelper helper += this, x, y, mouse_button, wheel_delta;

和得到的实际参数结果:

function mouse_wheel(sender, x, y, mouse_button, wheel_dalta)
end

鼠标滚轮动了之后,就会执行这个lua的脚本,灵活处理。

哪个更清晰直白?

不要批驳参数过多,这么多参数有时候也是需要的。

这些还不够说明吗?难道非得具体给你举个例子你才明白连续的push的不直观,与这个的直观表达的区别呢?

我写个push_back(1)到push_back(6),就说我设计上出问题了。

算了,问题都不集中在讨论Helper上了,还有什么意义呢?

2009/4/24 kusk <kuangs...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 9:34:02 AM4/24/09
to pon...@googlegroups.com
好了,我错了,你对了。

你的操作符的理论是正确的,我的错了。我不该用重载operator()操作符。

你的理论是正确的,我UI不该用AddMember这种方式。我错了,我该用AddControl或者其他的方式。

我错了。

2009/4/24 kusk <kuangs...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 9:51:45 AM4/24/09
to pon...@googlegroups.com
整个帖里面就只有两个人说了helper类的事情。

wing兄在每个贴里面都会有好的理论,这里谢谢他。

我在前面就说了:最近在写UI,所以就拿UI简单说一下。

然后三个人说我AddMember的方式有问题,我都说了,我简单说一下!

怎么不让我火大?有没有看我的标题?Helper类!是这个,不是UI的讨论。

另外很多人却把问题集中在:

重载()操作符和我这行代码上:


    win.add_members()
        ( btn )
        ( lbl )
        ( edt )
    ;

不爽你可以写成这样!

    win.add_members( btn );
    win.add_members( lbl );
    win.add_members( edt );

既然重载()这个就可以写成这样,难道有问题?

不爽,可以不重载,些个add或者push都可以。

用的到得地方就用,不用的地方就不用。

我想讨论的是Helper类,包括Helper类适用的地方。

重载操作符不是主要的问题。

不要怪我生气,我真的火大。这叫讨论吗?我的标题就没有看看?

我的标题
不是:重载()操作符的优劣。
也不是:我的UI设计方式。

是Helper类。

结束吧,别再让他上来了。

最后感谢:Ian Yang, YIngcai给我提示make_list_inserter,让我看到boost里面还有这么一系列的方式。

我现在可以修改我向脚本添加数据的时候,可以这样的形式添加,直观而且方便。

James Z.M. GAO

unread,
Apr 24, 2009, 10:12:17 AM4/24/09
to pon...@googlegroups.com
淡定, 有收获就好~

raymond

unread,
Apr 24, 2009, 10:03:28 AM4/24/09
to TopLanguage
这个例子根楼主的首帖很吻合,也让我弄明白了为什么添加button要做到helper里面,顾及了函数调用顺序的一致性,是一种解决办法。学习。
不过,要我解决,我会选择用for循环将参数逆序传进去,然后添加注释说明呵呵。
看这个帖子学习不少东西,
1)helper是宿主用来操作(比如添加)一些不变的对象,这点我总觉得根functional programming很像,可以重入的非业务相关
的方法都可以放到helper里面,貌似?
2)C++设计注意简洁性,可维护性,如果设计复杂的东西要有充分的理由,或者至少让大家都遵循这种规范。
谢谢各位

LeeoNix

unread,
Apr 24, 2009, 10:06:23 AM4/24/09
to pon...@googlegroups.com
是不是“很高深”的指出别人的例子代码设计的方式的优劣,然后很拔高自己的水平?

意思就是说,你写的例子问题好大,不利于阅读,不利于设计,开销好大。问题好多。

当我写下这个的时候,我真没意识到。我想找一些有关helper类设计思想和应用方式的讨论。

而不是关于我写的例子的讨论。

难道讨论点设计思维就这么难?

而且我在最后重点提到了:特别这种作为“初始化”方式的功能。

这是我所理解Helper类的应用方式的一种。

是不是我的表达有问题?如果我表达有问题,请你帮我指出来。

我不是那种不好说话的人,我也不是轻易发火的人。

我表达一个使用helper类的方式哪里有问题?请给我指出来。实在谢谢了。

LeeoNix

unread,
Apr 24, 2009, 10:29:56 AM4/24/09
to pon...@googlegroups.com
嗯。在我总结出的,比如添加,删除,

举个比较特殊的例子:还有读写属性这种,可以设计一个helper类。

这样设计不但非常容易和脚本结合,而且比较易读,可以写成类似:

a.name = "abc";
char* name = a.name();

这样的,就不用写get,set函数了。
而且看起来非常直观。
而两个操作符实际操作的是a里面的一个可能叫m_name的成员。

我不敢多说了,太复杂了。

真应了那句话:多说多错,少说少错,不说不错。

我都不知道错在哪。

我就知道我写这个属性多谢helper的时候,肯定会有人想说大家都关心的所谓的“效率”问题。

不信咱们走着瞧。

2009/4/24 raymond <shiq...@gmail.com>

Shuo Chen

unread,
Apr 24, 2009, 10:51:38 AM4/24/09
to TopLanguage
好文!透彻。

LeeoNix

unread,
Apr 24, 2009, 10:58:02 AM4/24/09
to pon...@googlegroups.com
你看这个叫kusk的话,看上去很无懈可击对不对?


>这样的代码,首先第一反应要考虑是不是设计上出问题了。我不认为增加运算符重载,或者其它方案这样的语法糖衣就能改进多少(相反,副作用可能更大)。
>比如,在这个pushback的例子里,如果数据之间有内在逻辑的关联性的话,首先考虑能不能由程序生成这些数据。如果可以,初始化的时候for一下由
>CPU生成当然是最简便的。

这个在说什么呢?他在说我用连续的调用push_back有“设计”问题。
而一句话都没有说helper还有那个叫list_inserter的东西。
我写这个只不过在解释使用这个东西等价的含义。
我举了个重载+=和重载逗号的例子给他看。
他看到的是连续的push_back和连续的123456。
好像我是一个初学的菜鸟一样,
很喜欢这样把连续的操作展开给别人看。

然后他又说了这个:


>假如不能(或者代价太大),说明这些数据很可能(如果不是一定的话)是资源性质数据,而非引导程序流程的普通变量。
>这样一来,为什么还要把这么些数据硬编码到代码段中,而不是分离开来?----比如说放到单独的数据资源段或者资源文件中然后由程序读入?
>要知道,资源数据硬编码到代码段中是设计上的大忌。应当避免。

他在说的我用1,2,3,4,5,6这6个数字有问题,是“硬编码”。

貌似现在的GCC可以这么干了:

vector<int> container{1,2,3,4,5,6}

是不是就说。这样写有一个问题,就是把123456写到代码里了,问题很大。
应该写到文件里,然后用vector读文件获得123456这样才是最好的方式。
而你看到的这个{ } 初始化的,干脆不要了。有什么意义?

什么是例子?例子就是写的人让别人看明白所表达的意思的。
如果我的“硬编码”有问题。那我写不出一个“软编码”的方式给你明白boost::assign,对不起。


2009/4/24 kusk <kuangs...@gmail.com>

qiaojie

unread,
Apr 24, 2009, 11:17:17 AM4/24/09
to pon...@googlegroups.com
大家有没有觉得楼主已经疯狂了?那就让他再疯狂一点吧。我就喜欢火上浇油,呵呵 
 
下面大家看楼主憋了半天举出来的这个例子:

luaHelper helper += this, x, y;

能把代码写成这样确实是不容易啊,有谁能看出来这个代码是在调用LUA的一个函数?楼主完全可以去参加难读程序比赛,把C++运算符统统重载一遍,保证没有人能看懂。
 
其实写成这样就可以了:
class LuaState
{
  lua_State* L;
 
public:
  template<T1>
  void Invoke(const char* func, const T1& p1);
 
  template<T1, T2>
  void Invoke(const char* func, const T1& p1, const T2& p2);
 
  template<T1, T2, T3>
  void Invoke(const char* func, const T1& p1, const T2& p2, const T3& p2);
 
 //here should be more
  ......
};
 
然后调用
LuaState.Invoke("mouse_move", this, x, y);
就可以了,多么
 
我知道楼主是刚学了boost库里面的make_list_inserter,急着拿出来显摆一下,可惜完全是东施效颦,画虎不成反类犬。
 

 
2009/4/24 LeeoNix <leeo...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 11:31:05 AM4/24/09
to pon...@googlegroups.com
不知道是哪个人说得这句话:哎..哎..哎....实在不应该参与到这种小白贴的讨论中....
 
嗯,是在9天前这个人说的。
 
请看到我这句话的人,看9天前某人的回答。
 
你是在不应该参加到这种“小白”讨论中来。何必呢。
 
你太不容易了,脸皮厚到一定程度了。
 
另外,你这个模板特化的这种,luabind,luabridge,sweet_lua之流,都比你些的好。
 
而且不用invoke某些东西。这例子我看多了。
 
而且最多支持8个参数的那种,你何必这么写呢。
 
看你这样的,还不如看这些成熟的代码。
 
我既然是小白,就不需要你来教了。请你远离,谢谢。

2009/4/24 qiaojie <qia...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 11:35:42 AM4/24/09
to pon...@googlegroups.com
所以你就这么写了。
 
LuaState.Invoke("mouse_move", this, x, y);
 
如果我有20个参数呢?
 
是不是要写20个这样的函数?
 
嗯,你是不是说我参数太多了?设计有问题?
 
既然你这么说,那我就举这个极端的例子,
 
我有20个参数,你也给我写20个这样的Invoke。
 
好像我不懂模板特化一样。
 
我发表的是小白贴,你实在不应该参与到这里来,
 
因为我太小白了。
 
2009/4/24 qiaojie <qia...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 11:39:24 AM4/24/09
to pon...@googlegroups.com
行行行,你设计的就是好的。我东施效颦,画虎不成反类犬。
 
你很牛,我很小白。
 
我用helper不如你不用helper设计的好。
 
行了吧,请你回吧。让这个话题结束吧。
 
您老的Invoke执行的模板用法很好很强大。
 
就当我刚才喝了点酒胡言乱语了。

2009/4/24 qiaojie <qia...@gmail.com>

qiaojie

unread,
Apr 24, 2009, 11:42:52 AM4/24/09
to pon...@googlegroups.com
哈哈哈哈,有时候逗逗小白还真是件好玩的事情,我在旁边看得都快笑喷了,楼主实在是太可爱了


 
2009/4/24 LeeoNix <leeo...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 11:43:08 AM4/24/09
to pon...@googlegroups.com
哎..哎..哎....实在不应该参与到这种小白贴的讨论中....
 
class Window
{
public:
    void add_members(const Button* value);
    void add_members(const Label* value);
    void add_members(const EditBox* value);
};
 
从来没见过有这么傻X的接口设计。如果我有几十种控件,难道我要写几十个add_members函数?
难道你的Button,Label,EditBox没有公共基类的?写一个void add_members(Control* value); 不就结了么?
 
居然还加const修饰作茧自缚,哪天你的child对象里有一个parent变量,add_members的时候需要set_parent了怎么办呢?难道修改接口?要么内部const_cast一下?
 
 
最后一贴。将这个叫qiaojie的人的原话顶上来。
 

LeeoNix

unread,
Apr 24, 2009, 11:46:01 AM4/24/09
to pon...@googlegroups.com
呵呵,让大家看清你的嘴脸才是最重要的。
 
如果这就是你的处事方式,还有必要让人对你尊重吗?
 
唉……
 
对于你的“逗小白”我只能叹口气。

2009/4/24 qiaojie <qia...@gmail.com>

qiaojie

unread,
Apr 24, 2009, 11:52:19 AM4/24/09
to pon...@googlegroups.com
想看Invoke 20个参数的时候代码怎么写?
 
那就再教你一招吧
 
class LuaState
{
  lua_State* L;
 
public:
  template<T1>
  void Invoke(const char* func, const T1& p1);
 
  template<T1, T2>
  void Invoke(const char* func, const T1& p1, const T2& p2);
 
  template<T1, T2, T3>
  void Invoke(const char* func, const T1& p1, const T2& p2, const T3& p2);
 
  void InvokeN(const char* func, Paramter param[], int count);
};
 
Paramter params[] = {p1, p2, p3, p4, ... , p20};
LuaState.InvokeN("call20", params, 20);
 
 


 
2009/4/24 LeeoNix <leeo...@gmail.com>

kusk

unread,
Apr 24, 2009, 12:11:56 PM4/24/09
to TopLanguage
嗯,这个例子的确有道理采用这样的流式重载。

虽然固然可以采用C/C++变长参数函数特性实现,但正如printf不如iostream(类型安全性、简洁性)一样,利用运算符的流式重载会更直观
且具有类型安全。

至于此类Helper的适用范围,依个人浅见,双目运算符其实相当于二元函数,但优势是复合调用时书写更简洁:a+b+c+d终归比add(add
(add(a, b), c), d)方便。所以对于可能要复合很多层的调用,即具有"若干数量不确定的不同类型的对象需要串行化输出(入)"语义的时
候都可以采用。

比如iostream可以认为是对不同对象的串行化IO;MFC的CArchive以及一个好像是用于网络数据传输的class(也是重载<<,>>)
亦类似;此例则是串行化输出到函数调用栈。

如果项目中大量用到和Lua交互的情况,这个抽象(自己实现抑或使用Boost)不失为一个比较通用的方案。对于经常会有这样需求的项目(群),统一作
为一个基本方案/库是很好的主意。

不过假若这类情况不够一般(比如所在的公司并非游戏公司也不是所有项目都与Lua挂接的话,或者就项目的代码规范干脆不允许运算符重载),"老朽"的土
法也并非没法解决。我个人还是倾向于使用如下老式Helper,把所有调用Lua函数的操作都封装成Helper函数(这里有一个期望,就是C++所调
用的Lua函数数量不应当太多,比如好几百个甚至上千个;如果太多,很可能不是一个好的设计),可能像这样:

void LuaState::mouse_wheel(Userdata *pSender, long x, long y) // 参
数瞎写的,没接触过Lua...
{
lua_pushuserdata(_state, this);
lua_pushnumber(_state, x);
lua_pushnumber(_state, y);
... // call mouse_wheel of Lua
}

这样一个基本的隔离就把Lua的调用封装起来了。坏处就是说这些Helper的实现本身还是一大堆的lua_pushnumber......不过个人认为其实
认为不是什么太大的问题。毕竟封装嘛,就是把脏的东东封装起来,统统打包到一个模块里就可以眼不见为净了。而除了封装者本人,不会有另外的人需要关心
Lua的函数如何调用、参数如何入栈的,甚至不知道有Lua的存在。万一哪一天完全不用Lua了,上层的东西一点也不用改:依然是最最平凡函数调用。而
如果用运算符重载,若是下面的实现哪天改成了用C,上面的运算符重载机制是不是还有必要保留(不留的话会理改接口,在大项目里面会牵连无数,留的话相当
于什么事没做会让后来看代码的人莫名其妙),也是一个问题。这也是坚持使用函数作为接口的好处。

> 2009/4/24 kusk <kuangsheng...@gmail.com>

> ...
>
> read more >>

qiaojie

unread,
Apr 24, 2009, 12:24:57 PM4/24/09
to pon...@googlegroups.com

void LuaState::mouse_wheel(Userdata *pSender, long x, long y)      // 参
数瞎写的,没接触过Lua...
{
       lua_pushuserdata(_state, this);
       lua_pushnumber(_state, x);
       lua_pushnumber(_state, y);
       ... // call mouse_wheel of Lua
}

这种方式虽然朴素,但其实也是可以的,尤其是跟lua交互量不大的情况下是非常简洁可靠的设计。所谓大道至简,有时候最简单最直接的方式往往也是最有效的方式。这也是为什么有些人会抛弃C++回去用C。当然我还是喜欢C++的语法糖,适量吃糖有益健康,过量了就要发福了。

learn

unread,
Apr 24, 2009, 12:27:36 PM4/24/09
to pon...@googlegroups.com
两位消消火,我看你们两位互相钉贴都好几周了,什么血海深仇至于这样嘛。对于不定参数,我觉得还是可以用c的变参机制,就像printf那样

在 09-4-24,qiaojie<qia...@gmail.com> 写道:

--
从我的移动设备发送

LeeoNix

unread,
Apr 24, 2009, 3:43:14 PM4/24/09
to pon...@googlegroups.com
看你说的真累,而且极没有条理性,
我都不太理解你到底在表达一个什么意思。
你是在说helper?还是其他的?
 
但是我看你写了这么多,我很不好意思不回答你。
毕竟你在对我回复,所以我针对你的回复,针对性的回答。
但,我希望讨论的不是这些,最好还是结束这个话题。
 
你写的这个:
void LuaState::mouse_wheel(Userdata *pSender, long x, long y)
这只是一种形式而已。
 
事实上,你所写的这个,刚开始我就是这么做的。
以前我会写一个叫ReadyToCallLuaEvent这么一个名字的函数去做这个,
具体是bool Window::ReadyToCallLuaEvent(const char* event_name, long x, long y);
 
针对不同的形式会做函数名重载。
功能和你说的类似,首先判断lua的event是不是存在,存在就顺序压栈,调用它。
这个独立于mouse_wheel之类的方法之外。
 
而我所得到的结果就是:增加代码量,增加出错几率。
对此,我已经吃亏有几个月了。
虽然在此讨论helper类并不是为了要解决这个问题,
但是前面的朋友提到的方法正好解决了我的问题。
再次感谢他的提点。
 
首先问问你:这个东西调用lua参数的东西到底谁来写?
我?还是我的继任者?还是我分配任务的下属?还是和我合作同级同事?
一般来说,谁设计的这个函数接口,谁就要写调用lua函数的方法。
 
我们把你说的尝试具体操作一下:
 
还是以这个UI的类,为例子,要添加mouse的处理和调用。我可以这么写。
一个纯粹的mouse_move方法还是不够扩展,我还可能有一个函数指针作为event来预备调用。
可以这么叫:
 
class window
{
public
  (void *mouse_move_event)(int x, int y);
protect:
  void mouse_move(int x, int y);
};

这个event有可能这样设计的时候用。
然后我再添加一个Lua的调用。
而这个调用,可能会写在这个event里面的。
也可能就直接写在mouse_move这个函数里,
本身这个mouse_move有可能被设计为虚函数,
由子类调用lua,这都是有可能的。
调用的位置有不同的选择。
 
如果有helper类,就这样子不变了。
 
如果按照你说的,添加一个调用的函数,拿就要在这两个的基础上添加一个调用的地方。 
好比叫:void set_mouse_down_param(window* sender, int x, int y);
 
我们把名字起的长一点。
 
class window
{
public
  (void *mouse_move_event)(int x, int y);
  void set_mouse_down_param(window* sender, int x, int y);
protect:
  void mouse_move(int x, int y);
};
 
这就是三个东西去用。嗯,你的想法看上去很好。 
 
不要说成百上千,就如果是10个操作的事件需要导出呢?不要奇怪绝对有这么多。
鼠标就可以4个,键盘有3个,拖放就有3个。
 
尝试写下这些的类型名:
 
mouse_move
mouse_down
mouse_up
mouse_wheel
key_down
key_up
key_press
start_drag
end_drag
drag_over
 
这里,mouse_down和mouse_up参数一样,key_down和key_up参数一样。
就是8个类型的参数入栈函数。就是要多加8个参数入栈的方法。
你看到了一个了,其他的呢?你真的希望写8个针对lua参数入栈的函数?
如果你还在坚持吗,你很有耐心的写这些东西。
而我现在已经不希望写这么多了。因为我需要编写的远比这些要多。
 
====
 
好,我们再说下一个,我前面提到了。
 
       lua_pushuserdata(_state, this);
       lua_pushnumber(_state, x);
       lua_pushnumber(_state, y);
 
我再次重申一下,就算我用lua有好一段时间了,我还是不可避免的把“栈”的思考方式带到函数参数上来,
因为有时候会长时间“想象”栈内容的抽象样子,是这个样子:
 
x
y
this
 
所以就顺手这么写了:
 
lua_pushnumber(_state, y);
lua_pushnumber(_state, x);
lua_pushuserdata(_state, this);
 
看上去不就是栈吗?其实正好是反的。
正好碰到在lua调用失败的时候,那还好说。
有时候根本就检查不出的错误。
 
或许你会做的很清晰,但我的头脑有时候会因为工作辛苦而僵化。
 
而你说得“封装”,还是“封装”这一系列入栈的顺序:
lua_pushuserdata(_state, this);
lua_pushnumber(_state, x);
lua_pushnumber(_state, y);
 
可以说,谁写谁就有几率传递出错。或许不怎么考虑lua本身问题的人出错低,但依旧会有几率。
倘若不是我在写这些,是我叫一个其他人写一个从window继承的类过来的其他功能类的话,他如果也和我一样写错怎么办?
本来我写的基础就可以避免这个,而我也叫他增加了工作量,而且增加了出错几率。
你有什么方法“一定”让别人拿到我的东西的时候,保证出错的几率低,还不用他来书写你所说的这类函数?
难道每次他写完就让我也添加一个相对应的参数入栈的方法?
 
我最不明白的就是,你站在一个什么立场去描述你所写下的示例。
底层库的设计者?具体应用的使用者?还是助手类的员工?
 
现在我的现实就是:
至少有我和另外一个同事写基础,
一个同事使用这个写编辑器,而他还没有用到这一块,
关键还有两个薪水较低的实习生写简单逻辑或者扩展。
倘若我这里写一个机制可以避免另外4个人出这样“低级”的错误,我为什么不去写?
 
所以我要解决这个问题,不仅仅用helper类来做这个,我这里有好几种备选方案,
类型列表模板是一个办法,可以根据Modern C++ Design里面描述的方法去写一个。
还有模板特化,C语言的va_list,都可以尝试。
 
既然用了lua,就不会让给我写扩展的人不知道lua这个东西。
而且你这句话极为的不现实:Lua的函数如何调用、参数如何入栈的,甚至不知道有Lua的存在。万一哪一天完全不用Lua了,上层的东西一点也不用改。
一点都不用改?至少你调用时候还是会修改。因为这是与开始耦合的地方。
ReadyToCallLuaEvent,我以前写的这个,至少有个合适的地方调用它吧。
 
哪天改成C?除非我有毛病。设计了一堆class,然后改用C。
看你说的话,很莫名其妙。把一些极端的话都说了,
既然在用C++讨论Helper类,helper class,什么时候改用C了?用C去讨论Helper类?
 
而你还是不理解我所说的helper,讨论依旧局限在操作符重载上,
用Java不一样设计Helper。其他的脚本语言没有这个,不还是一样设计Helper类。比如:Ruby
就像我举的第一个例子说Helper类一样,本身win.addMembers就可以直接写在window类里面一样。
这只是一种形式,我想找一些其他的替代方案,所以尝试用helper类。
 
既然用helper类,就有用helper类的道理。
 
最近这几天加班挺辛苦的,晚上的“时差”还是没调整过来,
2点半开始花了近一个小时写了这些回复。
你呢,随便看看也就是了。
 
我真正希望讨论的是helper,不是这些问题。
看你洋洋洒洒的说了这么多,就是不知道你说的重点是什么。
冲你辛苦写这么多,我也就认真回复一下。
 
2009/4/25 kusk <kuangs...@gmail.com>

LeeoNix

unread,
Apr 24, 2009, 3:55:35 PM4/24/09
to pon...@googlegroups.com
>这里有一个期望,就是C++所调用的Lua函数数量不应当太多,比如好几百个甚至上千个;如果太多,很可能不是一个好的设计
 
回答你这个期望。没有好几百,类型我大概数了下,是63个。以后还可能比这个更多。
2009/4/25 kusk <kuangs...@gmail.com>

莫华枫

unread,
Apr 24, 2009, 6:57:51 PM4/24/09
to pon...@googlegroups.com
几位别在争吵了,更不要作人身攻击。这不符合TL的规则。问题讨论到“我是怎么说的”“你是怎么看问题的”“谁谁谁小白”这种程度已经过了。你们的言论都有一些实在的内容,但却夹杂着这些无谓的言论,如果真的想在TL里学习知识,获得长进,请检要紧的说。


--
反者道之动,弱者道之用
m...@seaskysh.com
longsh...@gmail.com
http://blog.csdn.net/longshanks/

xxmplus

unread,
Apr 24, 2009, 8:55:10 PM4/24/09
to pon...@googlegroups.com
这骂战总算还是有点货色的,比纯喷子强多了

2009/4/25 莫华枫 <longsh...@gmail.com>:


> 几位别在争吵了,更不要作人身攻击。这不符合TL的规则。问题讨论到“我是怎么说的”“你是怎么看问题的”“谁谁谁小白”这种程度已经过了。你们的言论都有一些实在的内容,但却夹杂着这些无谓的言论,如果真的想在TL里学习知识,获得长进,请检要紧的说。
>
>
--

Any complex technology which doesn’t come with documentation must be the best
available.

Doyle

unread,
Apr 24, 2009, 10:16:19 PM4/24/09
to pon...@googlegroups.com
是你自己的帖子有问题吧
说是在讨论设计helper的思维
但是其中却有完全依赖冲在操作符,有'()','<<','+',','
然后说到lua设计help说到
"难道非得具体给你举个例子你才明白连续的push的不直观,与这个的直观表达的区别呢?"
那么你自己以为自己再说helper类的设计么?
你自己一直在重复的说重载重载,然后又不准别人说重载重载

作为看贴的我都觉得火大.

2009/4/24 LeeoNix <leeo...@gmail.com>:

--

Dan Quayle - &quot;I love California, I practically grew up in
Phoenix.&quot; -
http://www.brainyquote.com/quotes/authors/d/dan_quayle.html

Doyle

unread,
Apr 24, 2009, 10:35:37 PM4/24/09
to pon...@googlegroups.com
耐心的看完了

中途把例子中的重载()变成了push
似乎总算说明楼主是想要说设计成操作总是返回自身的helper类的好处
就是可以写成链式的
但是后边又举例lua中用重载"+="和","后的形式helper+=this,x,y
来代替


lua_State* L = luaL_newstate();
lua_pushuserdata(L, this);
lua_pushnumber(L, x);
lua_pushnumber(L, y);

那么,似乎又再说,设计一个有重载运算符,使得看上去是一种新语法的helper类的好处
再加上一上来一楼的例子

所有人都有理由理所当然的认为你是在说后一种helper类
于是理所当然的都集中在重载运算符来窗在新语法这个问题上了

实际上楼主应该去设计DSL而不是设计什么helper类

chaoyan ma

unread,
Apr 24, 2009, 11:12:28 PM4/24/09
to pon...@googlegroups.com

总算有点明白了,楼主是在考虑 用输入参数流 调用不定参数方法初始化的问题?

kusk

unread,
Apr 24, 2009, 11:17:53 PM4/24/09
to TopLanguage
谢谢你的耐心。抱歉可能不太明白事件event是怎么交互的。依我原来的理解,写63个包装函数(其实可以包得原始一些,C风格的全局函数就够了,上层
可以再作抽象)足够了。即使每个函数里可能有8个push,在我看来是可以接受的(当然,只是个人观点)。因为逻辑上足够简单,UT也很好测。而我看来
也是一种Helper(亦即没有偏离"Helper"之探讨,不过可能略嫌过泛)。我完全不反对Helper,唯一的顾虑只是"用到运算符重载"这样的
接口形式的Helper. 所以我(自认为)一直在探讨如何使用基本的函数接口的形式来做Helper(变长参数函数或者前面说的土法),毕竟像
Java没有运算符重载能力,也一样很好用。不过既然已经偏离主题,而且前面该表达的我自己也表达了,再者可能我们沟通上或许有问题(可能是我表达&理
解能力不够呵呵),我赞成关闭这条探讨。

楼主用新式的方法解决了问题,就已经足够,大家应该随喜。至于说楼主你发起的讨论的中心好像吸引不到大家(如楼主说的总是出现偏离主题的人群,
which我相信是无意的,包括我自己在内^_^. ),我斗胆猜测,可能和C++社群近年返朴的趋势有关:开帖看到win.foo(btn1)
(xx)(yy)这样的设计对于抽象过敏型的人而言还是蛮震惊的;而且同一个抽象机制,对于一个例子可能是过于聪明,但另一些例子则不是。这也是为什么
我一直针对你的例子回复的原因:并非抓着细节把柄不放,而是说如果没有具体的例子,完全没有办法判断优劣,甚至于说有可能是不佳的设计才导致了过于聪明
的用法。楼主一开始如果举出压栈封装的例子,相信观众会好接受许多。大家出现偏题理解,想必大多无心,还望楼主原谅。

至于说一些情绪化的回帖,我也是坚决不赞成的,不管其技术立场、能力如何。也不断有朋友在提醒了(啊,我希望自己不是被"提醒"的其中一员,我可一直是
对事讨论的,虽然可能不是很合题意呵呵)。跟自认为强的人、自己敬佩的人探讨,保持情绪上的中立(甚至崇敬),完全不难。但不够,要做到跟自认为(仅仅
是自认为!)某些技术领域不如自己的人讨论的时候还要保持就事论事、对事不对人,才是专业的探讨问题的态度。出现谁也不服谁演化到人身攻击式的"讨
论",实在应该引起大家的注意,毕竟楼主是讨论发起人,不管技术立场如何,都应该尊重。

On Apr 25, 3:55 am, LeeoNix <leeoni...@gmail.com> wrote:
> >这里有一个期望,就是C++所调用的Lua函数数量不应当太多,比如好几百个甚至上千个;如果太多,很可能不是一个好的设计
>
> 回答你这个期望。没有好几百,类型我大概数了下,是63个。以后还可能比这个更多。

> 2009/4/25 kusk <kuangsheng...@gmail.com>

> ...
>
> read more >>

LeeoNix

unread,
Apr 25, 2009, 5:43:59 AM4/25/09
to pon...@googlegroups.com
你说的“一开始就有Push”的例子。

这个,也是后面应用的时候发现的。

要不是某个朋友提醒,我还会继续研究模板特化呢。

重载()操作符,其实是用对象名和函数名保持一致,这样方便理解。

而我写了(xx)(yy),这种就让很多人被雷到了。

2009/4/25 kusk <kuangs...@gmail.com>

LeeoNix

unread,
Apr 25, 2009, 5:45:57 AM4/25/09
to pon...@googlegroups.com
helper这个词的意思是否明白?

用google搜索一下。

helper类有很多种形式,我只是说了一种而已。

关键字,helper,不是具体某个问题,而是helper类设计的问题。

2009/4/25 chaoyan ma <chaoyan.ma@gmail.com>

总算有点明白了,楼主是在考虑 用输入参数流 调用不定参数方法初始化的问题?


LeeoNix

unread,
Apr 25, 2009, 5:48:55 AM4/25/09
to pon...@googlegroups.com
再次提醒你,我并不是要专注于某一个特定的问题去找寻解决方案。

你还是以为我是去找特定的问题的解决方案,我还是会用google去搜索的。

而且我本来写Helper类的地方,就不是这个连续push的地方,

是其他地方,感觉这样写比较灵活,所以拿出来大家讨论一下。

我就没想到,Helper类是个什么东西,就几个人看明白而已。

2009/4/25 kusk <kuangs...@gmail.com>

qiaojie

unread,
Apr 25, 2009, 6:17:28 AM4/25/09
to pon...@googlegroups.com
这个楼主的逻辑是非常混乱的。既然他要谈helper类,那么当然要举例说明了,可是偏偏他就举了几个不好
的例子来,不但说明不了问题,反倒是让人感觉与其这样用helper还不如不用。于是有好心人给他指出例子
里的问题,结果他就莫名其妙的恼了,他说“你们凭无视我的论点,专挑我例子里的毛病?” 搞的大家都觉得
很莫名其妙。
楼主之所以这样逻辑混乱,只是因为他原本只是想显摆一下他最新研究出来的C++小技巧,他想听到的只是
“写的太好啦”,”楼主英明"之类的溢美之词,可没想到上来就被泼了几盆冷水,其恼羞成怒自然也就容易理
解了。





2009/4/25 Doyle <doyl...@gmail.com>

qiaojie

unread,
Apr 25, 2009, 6:27:23 AM4/25/09
to pon...@googlegroups.com
在C++里用helper类来得到一些便利性本是件非常正常的事情,本来就不值得有什么好多说的。
可是偏偏就有人拿出几个写的很烂的helper类出来说helper的好处,别人给他指出问题了,他还
反问人家“你们懂不懂什么叫helper啊?请跟我谈helper,不要来我例子里的毛病!”



2009/4/25 LeeoNix <leeo...@gmail.com>

LeeoNix

unread,
Apr 25, 2009, 7:14:15 AM4/25/09
to pon...@googlegroups.com
如果邮件列表都像你这样态度和口气说话。

那根本就没有存在的必要。

以后将无视任何从qia...@gmail.com发过来的邮件。

敬请注意。

2009/4/25 qiaojie <qia...@gmail.com>

LeeoNix

unread,
Apr 25, 2009, 7:17:29 AM4/25/09
to pon...@googlegroups.com
而且那个叫:Kenny Yuan的朋友举个例子,你也要评论一番,批驳一番。

反正都小白,你很厉害就是了。对不对?

最后人家就干脆什么话都不回了,你不认为你态度有问题吗?

还很占理得样子。真是可笑。

2009/4/25 LeeoNix <leeo...@gmail.com>

qiaojie

unread,
Apr 25, 2009, 7:30:56 AM4/25/09
to pon...@googlegroups.com
难道这个论坛是你开的?还是这个论坛有规定不准评论和批驳别人的观点?
论坛起争论本来就是很正常的事情,有理说理,就事论事而已。发小白贴就
是得接受批评,我批评的有没有根据有没有道理,大家都看得到。你这人怎
么就那么喜欢搬弄是非呢?




2009/4/25 LeeoNix <leeo...@gmail.com>

chaoyan ma

unread,
Apr 25, 2009, 8:25:45 AM4/25/09
to pon...@googlegroups.com
:-)  qiaojie和LeeoNix是不是认识的啊?可能是这方面的实践很少的缘故吧,看了半天也不太懂~ 
LeeoNix 独立出Helper类是不是考虑将一些类可能不会用到的静态资源放给Helper类管理,而当需要这些资源的方法(作为Helper的方法)
被调用的时候再通过Helper的构造函数申请,Helper类自然也可以在类的析构前提前释放资源
 
总的来说,Helper类是不是充当了资源的管理角色?在并发情况下 Helper内也可以进行访问控制
LeeoNix ,偶承认自己是小白  说错的地方希望能提醒一下 不要骂我啊

chaoyan ma

unread,
Apr 25, 2009, 9:07:26 AM4/25/09
to pon...@googlegroups.com
~~ 不懂就不懂把 还要说个不太懂 我还真够虚伪的 

Doyle

unread,
Apr 25, 2009, 9:46:17 AM4/25/09
to pon...@googlegroups.com
同意.

2009/4/25 qiaojie <qia...@gmail.com>:

--

H. L. Mencken - &quot;It is even harder for the average ape to
believe that he has descended from man.&quot; -
http://www.brainyquote.com/quotes/authors/h/h_l_mencken.html

LeeoNix

unread,
Apr 25, 2009, 9:41:30 AM4/25/09
to pon...@googlegroups.com
如果宿主有100个方法,但是版本不允许做大修改,方法太多又看着头疼,

可以独立出某一类类型的操作,为一个Helper类出现。

Helper类就好像一个“分类”一样,降低宿主本身的接口数,添加类似接口的时候,就不要修改宿主了。

这是一个比较实用的重构方法,呵呵,倘若宿主本身不能做比较大的修改的话。

不过我还是感觉添加和读写的时候,使用helper类是很有趣的设计方法。

不至于让宿主本身的接口膨胀。

不过具体是否使用它,还得看具体的业务分析。

LeeoNix

unread,
Apr 25, 2009, 9:50:47 AM4/25/09
to pon...@googlegroups.com
对了,忘了说了。我不认识这个人,第一次碰到。不要管他。

LeeoNix

unread,
Apr 25, 2009, 9:25:02 AM4/25/09
to pon...@googlegroups.com
你说的没错。感谢啊,终于有个人看标题了,我感动死了。

T_T

感动的我想给你个拥抱+飞吻。
没事,我也被人说小白了,我们都是小白,小白们之间同乐同乐。

Helper类有个适用的地方就是这里。所以我的标题写的是:

在添加或者读写的时候,设计一个Helper类感觉很有趣

而我说的“读写”二字正是你所说的资源管理。难道我写“读写”二字别人看不明白?那就是我的失误了。

比方说:我有个需要从XML获得的东西,如果写成Helper类的话,那这样宿主就与XML本身无关了。
不过实际应用上,保证真正与XML无关也是不现实的,
宿主还是得有个总的是否使用XML的开关之类的控制语句,或者是调用XML还是其他类型的Helper的控制。
但其好处就是XML的具体解析和使用,宿主已经不需要操心了。

而Helper类接收的只是宿主的部分信息。

举个例子:

struct WindowHelper
{
    void readFromXML(/*某些需要初始化的宿主参数*/);
    //或者干脆重载()操作符,直接用Helper类本身的名字去执行。
    //比如我要针对name做一下读写。
    void operator(const std::string& name);
};

class Window
{
public:
    //返回值不做控制,根据需要。用void也可以,返回Helper本身的引用也可以。
    WindowHelper& readLayout();
};

只是写了read,或许有个write相对应。
这是单个Helper的形式。
或者换个视角,上面是以读写为视角的写法。

以单独的属性为视角。
每个属性都作为一个Helper出现,
宿主保留一个map,用名字去提取helper。
依旧保持宿主与属性的读写方式无关。

class PropertyHelper
{
    static std::string& get(Window* sender);
    static void set(Window* sender, std::string& name);
}

struct less_str
{
    bool operator()(const char* s1, const char* s2)
    {
         return strcmp(s1, s2) < 0;
    }
}

class Window
{
    map<const char*, PropertyHelper*, less_str> _property_map;
public:
    PropertyHelper& get_property(const char* name);
};

然后这些属性就可以做注册处理,这样XML文件就可以做的很灵活。
再添加属性的时候,从helper基础继承下来,然后注册进_property_map里面。
这样宿主不需要做任何修改,而保持原有的版本。
看来还是需要另开贴说明白一些。时间根本不能消耗在和那种无理的人争吵上。

持续感动。送上飞吻+拥抱。

有人理解就好。我继续做我的事情了。:)

chaoyan ma

unread,
Apr 25, 2009, 9:58:05 AM4/25/09
to pon...@googlegroups.com

~~~ ~~~ 晕 楼主举的例子好像误导大众了

LeeoNix

unread,
Apr 25, 2009, 10:02:25 AM4/25/09
to pon...@googlegroups.com
我以为Helper这个单词就足够解释了,这个说明不了问题?

最近实在很忙,回头再开个贴写的详细点。

2009/4/25 chaoyan ma <chaoyan.ma@gmail.com>

~~~ ~~~ 晕 楼主举的例子好像误导大众了


Eric.Wang

unread,
Apr 25, 2009, 11:29:38 AM4/25/09
to TopLanguage
在我的理解里,helper不是做这个用的吧?
拿微软的SqlHelper来说,起的作用主要是做一层易用性封装。因为基础工具库的设计目标是对底层功能的映射,未必满足上层使用便捷的要求。中间加
一个helper,就可以让底层关注功能实现,上层得到易用性。

如果一个class确实需要有100个方法,我觉得应该尽量把方法分类写清楚,而不是把方法放到另外一个class去。比如CWnd,里面林林总总的方
法是很多,但也没有谁把里面的OnPaint方法就写到另外一个class去。

我不知道我们两个说的"helper"是不是同一个东西。另外,"helper"是一个有特定含义的专有名词吗(像interface、thread那
样)?

把一个class的member function写到另外一个class去,伴随着的多半是把member var从private声明为
protected,并且把另外那个class声明为friend。这只是形式上好看了一点,耦合性和复杂度并没有降低。逻辑既然还是老样子,该出的错
还是照样会出的。


On 4月25日, 下午9时41分, LeeoNix <leeoni...@gmail.com> wrote:
> 如果宿主有100个方法,但是版本不允许做大修改,方法太多又看着头疼,
>
> 可以独立出某一类类型的操作,为一个Helper类出现。
>
> Helper类就好像一个"分类"一样,降低宿主本身的接口数,添加类似接口的时候,就不要修改宿主了。
>
> 这是一个比较实用的重构方法,呵呵,倘若宿主本身不能做比较大的修改的话。
>
> 不过我还是感觉添加和读写的时候,使用helper类是很有趣的设计方法。
>
> 不至于让宿主本身的接口膨胀。
>
> 不过具体是否使用它,还得看具体的业务分析。
>

> 2009/4/25 chaoyan ma <chaoyan...@gmail.com>

王辉

unread,
Apr 25, 2009, 7:21:35 AM4/25/09
to pongba
不要这个样子了吧,问题拿出来就是讨论的,发表自己的见解,对别人的看法也多些包容就好了。
 
 
2009-04-25


发件人: LeeoNix
发送时间: 2009-04-25  19:17:52
收件人: pongba
抄送:
主题: [TL] Re: {技术}在添加或者读写的时候,设计一个Helper类感觉很有趣。

Jack.Chu

unread,
Apr 25, 2009, 10:22:53 AM4/25/09
to TopLanguage
有点赞同啊~~

至于那个Helper类,我觉得要么它是一个对象的wrapper,要么其函数大部分都是static的。这样可以把部分逻辑分开,虽然有好处,但是并
不明显。

真正值得争论的,应该是发现一个pattern,在工作中为了解决某一问题而研究出来的比较好的范式。拿出来讨论之后,多方推敲,反复修改,看客和自己
都有所收获,才是目的。


On 4月25日, 下午6时17分, qiaojie <qiao...@gmail.com> wrote:
> 这个楼主的逻辑是非常混乱的。既然他要谈helper类,那么当然要举例说明了,可是偏偏他就举了几个不好
> 的例子来,不但说明不了问题,反倒是让人感觉与其这样用helper还不如不用。于是有好心人给他指出例子
> 里的问题,结果他就莫名其妙的恼了,他说“你们凭无视我的论点,专挑我例子里的毛病?” 搞的大家都觉得
> 很莫名其妙。
> 楼主之所以这样逻辑混乱,只是因为他原本只是想显摆一下他最新研究出来的C++小技巧,他想听到的只是
> “写的太好啦”,”楼主英明"之类的溢美之词,可没想到上来就被泼了几盆冷水,其恼羞成怒自然也就容易理
> 解了。
>

> 2009/4/25 Doyle <doyle...@gmail.com>

LeeoNix

unread,
Apr 25, 2009, 11:59:13 AM4/25/09
to pon...@googlegroups.com
看我标题:

[TL]{技术}在添加或者读写的时候,设计一个Helper类感觉很有趣。

我在标题里就写了四个字:添加、读写。
另外:Helper。

既然是助手类。其实就是把宿主的可以用的方法独立出来而已。

另外一种选择就是把这些写在某个类里面。可以选择助手,也可以选择直接写进类。
具体是否用Helper,要看具体的业务。不是一概而论的。

而我在标题里面明确提到了:“在添加或者读写的时候”。
倘若大家没有看到这一行字,我也就不想参与这个了。
我不知道是我标题写的不对,还是我表达的逻辑有问题。
如果我刚才说的那个属性读写的Helper的方法,
还不能让你明白的话,那可能就是我表达的问题。
可能以后我会再开一个帖子来说明。
也可能以后我在发表标题之后,要在里面用最大的字外加粗体去写清楚我的意思。
又是不是我没有明确表达一定要Add的类型,或者写get、set这样类型的函数给大家看,所出的问题?
感觉我写的这这么大一个标题已经变得没用了。

2009/4/25 Eric.Wang <wangsh...@gmail.com>

LeeoNix

unread,
Apr 25, 2009, 12:01:00 PM4/25/09
to pon...@googlegroups.com
包容。你看他的话:

>哎..哎..哎....实在不应该参与到这种小白贴的讨论中....

是啊,我小白贴,他何必自降身份来讨论小白贴呢?

你自己说说看他以前说的这句话。

什么叫厚脸皮?

2009/4/25 王辉 <wangh...@gmail.com>

Eric.Wang

unread,
Apr 25, 2009, 12:05:14 PM4/25/09
to TopLanguage
能不能结合你的实际工作,举一个比较靠谱的例子出来?

class A
{
friend class AHelper;

protected:
int x;
}

class AHelper
{
int GetX(A* a) { return a->x; };
void SetX(A* a, int val) { a->x = val; };
}

上面这个例子是你想表达的意思吗?


On 4月25日, 下午11时59分, LeeoNix <leeoni...@gmail.com> wrote:
> 看我标题:
> [TL]{技术}在添加或者读写的时候,设计一个Helper类感觉很有趣。
> 我在标题里就写了四个字:添加、读写。
> 另外:Helper。
>
> 既然是助手类。其实就是把宿主的可以用的方法独立出来而已。
>
> 另外一种选择就是把这些写在某个类里面。可以选择助手,也可以选择直接写进类。
> 具体是否用Helper,要看具体的业务。不是一概而论的。
>
> 而我在标题里面明确提到了:"在添加或者读写的时候"。
> 倘若大家没有看到这一行字,我也就不想参与这个了。
> 我不知道是我标题写的不对,还是我表达的逻辑有问题。
> 如果我刚才说的那个属性读写的Helper的方法,
> 还不能让你明白的话,那可能就是我表达的问题。
> 可能以后我会再开一个帖子来说明。
> 也可能以后我在发表标题之后,要在里面用最大的字外加粗体去写清楚我的意思。
> 又是不是我没有明确表达一定要Add的类型,或者写get、set这样类型的函数给大家看,所出的问题?
> 感觉我写的这这么大一个标题已经变得没用了。
>

> 2009/4/25 Eric.Wang <wangshaoq...@gmail.com>

LeeoNix

unread,
Apr 25, 2009, 12:05:53 PM4/25/09
to pon...@googlegroups.com
所以我请求这个叫qiaojie的人。

您是高手,以后看到我发的贴,请你不要自降身份参加好吗?

我是小白,发表的是小白贴。

您老已经哎,哎,哎了,也说了“实在不应该参与到这种小白贴的讨论中”

我不知道您老为什么脸皮这么厚,非得要参与。

您老把我的名字拉到黑名单里不行吗?

既然看见我就烦,那何必还参与?何必呢?

一开始你就知道是小白贴了。那是在10天以前了

10天之后你还来,何必呢?

我真服了你了,你何必和我这小白计较呢?

LeeoNix

unread,
Apr 25, 2009, 12:08:19 PM4/25/09
to pon...@googlegroups.com
我在前面已经举了一个针对连续入栈的helper方式了。

如果你没看到的话,那我在写一个get,set函数的。

稍等。我的代码在内网机器上,不能复制,我只能比着,写个大概。

2009/4/26 Eric.Wang <wangsh...@gmail.com>

Eric.Wang

unread,
Apr 25, 2009, 12:24:36 PM4/25/09
to TopLanguage
连续入栈的那个例子我看到了。我觉得那不是一个很好的例子。我倾向于使用笨一点的办法:
wnd.add_member(btn);
wnd.add_member(lbl);
wnd.add_member(edit);

add_members这不是一个好接口,如果一定要,我更倾向于下面的方式:
有RTTI:
//return how many members are added
int add_members(CWnd* members, int member_count);

没有RTTI的话:
class MemberInfo
{
char type;
void* member;
}
//return how many members are added
int add_members(MemberInfo* members, int member_count);


On 4月26日, 上午12时08分, LeeoNix <leeoni...@gmail.com> wrote:
> 我在前面已经举了一个针对连续入栈的helper方式了。
>
> 如果你没看到的话,那我在写一个get,set函数的。
>
> 稍等。我的代码在内网机器上,不能复制,我只能比着,写个大概。
>

> 2009/4/26 Eric.Wang <wangshaoq...@gmail.com>

It is loading more messages.
0 new messages