[AHK]用AutoHotkey面向对象实践“简单工厂模式”

8 篇文章 0 订阅
6 篇文章 0 订阅

用面向对象编程实现一个计算器程序,要求输入两个数和运算符号,得到结果。


“所有编程初学者都会有这样的问题,就是碰到问题就直觉地用计算机能够理解的逻辑来描述和表达待解决的问题及具体的求解过程。这其实是用计算机的方式去思考,比如计算器这个程序,先要求输入两个数和运算符号,然后根据运算符号判断如何运算,得到结果,这本身没有错,但这样的思维却使得我们的程序只为满足实现当前的需求,程序不容易维护,不容易扩展,更不容易复用。从而达不到高质量代码的要求。”
例子:【活体印刷】这里写图片描述

三国时期,由于还未发明活字印刷,所有要改字的时候,就必须要整个刻板全部重新刻!实在是很吐血很蛋痛!而到了后来,活体印刷的出现,此景此情此处只需更改四字即可,其余工作都未白做,岂不妙哉!
“第一,要改,只需更改要改之字,此为可维护;第二,这些字并非用完这次就无用,完全可以在后来的印刷中重复使用,此乃可复用;第三,此诗若要加字,只需刻字加入即可,这是可扩展;第四,字的排列其实可能是竖排也可能是横排,此时只需将活字移动就可做到满足排列需求,此是灵活性好。”
“可维护、可复用、可扩展、灵活性好这四点,不仅仅是面向对象编程的一个起始点更是我们对待每一个程序应用一个较好的编程思想!”

————————————————

基于以上的情景,要求你再写一个Windows的计算器,你现在的代码能不能复用呢?
“有人说直接把代码复制过去不就可以了吗?改动不多不算麻烦。这其实是非常不好的编码习惯,因为当你的代码中重复的代码多到一定程度,维护的时候可能就是一场灾难。越大的系统,这种方式带来的问题越严重,编程有一原则,就是用尽可能的办法去避免重复。回过头看看我们写的代码,有哪些是和控制台(界面逻辑)无关的,而哪些又与计算器(业务逻辑)有关呢?
“准确地说,就是让业务逻辑与界面逻辑分开,让它们之间的耦合度下降。只有分离开,才可以达到容易维护或扩展。”

业务与界面分离

/**
*运算类
*@author sunwind
*/

class Operation {
    GetResult(numberA, numberB, operate)
    {
        result := 0d
        switch (operate)
        {
         case "+":
             result := numberA + numberB
             
         case "-":
             result := numberA - numberB
             
         case "*":
             result := numberA * numberB
             
         case "/":
         {
             if (numberB=0) 
                throw { what: "除零错误!", file: A_LineFile, line: A_LineNumber }
             else
                result := numberA / numberB     
         }              
             
        }
        return result
    }
}


/**
 * 业务与界面分离
 * @author sunwind
 *
 */
class Test {
    main() {        
        try {
            InputBox, strNumberA , 输入, 请输入数字A:
            InputBox, strOperator , 输入, 请选择运算符号(+、-、*、/):
            InputBox, strNumberB , 输入, 请输入数字B:
            strResult := Operation.GetResult(strNumberA,strNumberB, strOperator)
            MsgBox % "结果是:"  strResult
        } catch e {
            MsgBox % "您的输入有错:"  e.what "`n"  e.file "`n"  e.line
        }
    }
}

Test.main()

如此的代码编程,不单是Windows程序,Web版程序需要运算也可以用它,PDA、手机等需要移动系统的软件需要运算也可以用它。但仅此而已,还谈不上完全面向对象,此处仅仅只用了面向对象三大特性中的其中一个(封装),还有两个没用(继承和多态)呢!

紧耦合 VS 松耦合

:“基于以上的代码,是否已做到很灵活的修改和扩展呢?”
条件:“现在如果我希望增加一个开根(sqrt)运算,你会如何改?”
菜鸟回答:“那只需要改Operation类就行了,在switch中加一个分支就行了。”
分析:“问题是你要加一个平方根运算,却需要让加减乘除的运算都得来参与编译,如果你一不小心把加法运算改成了减法,这岂不是大大的糟糕。打个比方,如果现在公司要求你为公司的薪资管理系统做维护,原来只有技术人员(月薪),市场销售人员(底薪+提成),经理(年薪+股份)三种运算算法,现在要增加兼职工作人员(时薪)的算法,但按照上述的程序写法,公司就必须要把包含原三种算法的运算类给你,让你修改,你如果心中小算盘一打,‘TMD!公司给我的工资这么低,这下有机会了’,于是你除了增加了兼职算法以外,在技术人员(月薪)算法中写了一句。

if (技术人员是我)
{
    salary *= 1.1;
}

如此就意味着,你的月薪每月都会增加10%(小心被抓去坐牢),本来是让你加一个功能,却使得原有运行良好的功能代码产生了变化,这个风险太大了!”

简单工厂模式

工厂模式用于处理 如何获取实例对象问题,建造者模式用于处理如何建造实例对象 问题。

工厂方法的实现并不能减少工作量,但是它能够在必须处理新情况时,避免使已经很复杂的代码更加复杂。

通常设计应该是从工厂方法开始,当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。当设计者在设计标准之间进行权衡的时候,了解多个创建型模式可以给设计者更多的选择余地。
 

 

#SingleInstance, Force
/**
 * 运算类
 * @author sunwind
 *
 */
class Operation {
    _numberA
    {
        get{
            return this._numberA
        }
    }
    _numberB
    {
         get{
            return this._numberB
        }
  
    }
}
/**
 * 加减乘除类 -- 加法类,继承运算类
 * @author sunwind
 *
 */
class OperationAdd extends Operation {   
   
    result[]
    {
        get{
            return base._numberA + base._numberB
        }
    } 
}
/**
 * 加减乘除类 -- 减法类,继承运算类
 * @author sunwind
 *
 */
class OperationSub extends Operation {
    result[]
    {
        get{
            return base._numberA - base._numberB
        }
    } 

}
/**
 * 加减乘除类 -- 乘法类,继承运算类
 * @author sunwind
 *
 */
class OperationMul extends Operation {   
    result[]
    {
        get{
            return base._numberA * base._numberB
        }
    } 
}
/**
 * 加减乘除类 -- 除法类,继承运算类
 * @author sunwind
 *
 */
class OperationDiv extends Operation {   
   
    result[]
    {
        get{
            return base._numberA / base._numberB
        }
    } 
}
;增加一个新运算,需要增加一个Operation的子类 比如,增加“指数运算”  就幢一个 OperationExp 类 继承 Operation
;并需要在简单运算工厂类中增加相应运算符即可。
class OperationExp extends Operation{
    
    result[]
    {
        get{
            return base._numberA ** base._numberB
        }
    } 
}

/**
 * 简单运算工厂类
 * @author sunwind
 * 
 */
class OperationFacotry {
    createOperate(operate){      
        oper :="" 
        switch (operate)
        {
            case "+":
                oper := new OperationAdd()
            case "-":
                oper := new OperationSub()
            case "*":
                oper := new OperationMul()
            case "/":
                oper := new OperationDiv()
            case "^":
                oper := new OperationExp()
        }
        return oper
    }   
}
/**
 * 客户端代码
 * @author sunwind
 *
 */
class Test{
    main() {
        oper:= ""
        oper := OperationFacotry.createOperate("^")
        oper._numberA:=2
        oper._numberB:=3
        result := oper.result
        MsgBox % "结果是:"  result
    }
}


Test.main()

编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更容易维护,容易扩展和复用,只有这样才可以真正得到提高。写出优雅的代码真的是一种很爽的事情。UML类图也不是一学就会的,需要有一个慢慢熟练的过程。所谓学无止境,其实这才是理解面向对象的开始呢。
——程杰《大话设计模式》

参考:《大话设计模式》之简单工厂模式(Java版)_J1012的博客-CSDN博客_大话设计模式java版 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 你可以在 Autohotkey 脚本内使用 Reload 函数来重新加载它自己。该函数的语法如下: Reload 使用该函数的示例如下: ^r:: Reload return 上面的示例中,当用户按下 "Ctrl + R" 组合键时,脚本将会重新加载自身。 ### 回答2: AutoHotkey(简称AHK)是一种自动化脚本语言,常用于自动化操作、自定义快捷键等。如果你想要让一个AHK文件在运行时重新加载自己,可以使用以下方法。 首先,在你的AHK脚本中添加一个快捷键绑定,用于触发重新加载。你可以选择任意一个你喜欢的快捷键,比如"Ctrl + Alt + R"。添加以下代码可以实现这个功能: ^!r:: Reload return 第一行代码中的"^"代表Control键,"!"代表Alt键,"r"代表"R"键。所以 "^!r"即为"Ctrl + Alt + R"。第三行的"Reload"命令用于重新加载脚本。 将以上代码添加至你的AHK脚本中,保存修改。 接下来,在你打开的AHK脚本中,按下设置的快捷键"Ctrl + Alt + R"即可重新加载脚本。这样,你就不需要关闭脚本再重新打开它了,可以直接加载修改后的内容。 需要注意的是,重新加载脚本会终止当前脚本的执行,并重新启动脚本。 希望以上解答对你有所帮助。如有其他问题,请随时提问。 ### 回答3: 要让AutoHotkeyAHK)文件重新加载自己,你可以使用AHK内置的Reload函数。Reload函数可以重启当前运行的脚本,即重新加载脚本文件。 你可以在脚本文件的任何位置使用Reload函数来实现重新加载。以下是一个示例: ```autohotkey ^!r:: Reload return ``` 上述代码将Ctrl+Alt+R绑定到Reload函数。当你按下这个组合键时,AHK脚本会重新加载。 如果你想要自动重载脚本,你可以将Reload函数与文件监视器结合使用。文件监视器可以监视脚本文件的任何更改,并在更改后自动重新加载。 以下是一个将文件监视器与Reload函数结合使用的示例: ```autohotkey #Persistent SetTimer, ReloadCheck, 1000 return ReloadCheck: If (FileExist("脚本文件的完整路径")) { FileGetTime, currentModified, 脚本文件的完整路径 If (currentModified != previousModified) { Reload } } previousModified := currentModified return ``` 在上述代码中,我们首先设置了一个持续的定时器(SetTimer),它每秒钟检查一次脚本文件是否有更改。然后,我们使用FileExist和FileGetTime函数来获取脚本文件的最新修改时间,如果最新修改时间与之前保存的修改时间不同,则重新加载脚本。 请注意,你需要将代码中的"脚本文件的完整路径"替换为你实际的脚本文件路径。 这些是使用AutoHotkey重新加载脚本的两种方法。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值