前言
小菜去面试,前台给他一份题,上面写道"请使用面向对象语言实现一个计算器控制台程序,要求输入两个数,得到结果"
小菜的第一次修改
<?php
$A = $_POST['A'];
$B = $_POST['B'];
$C = $_POST['C'];
$D = '';
if($B == '+')
$D = round($A + $C ,2);
if($B == '-')
$D = round($A - $C ,2);
if($B == '*')
$D = round($A * $C ,2);
if($B == '/')
$D = round($A / $C ,2);
echo '结果是: '. $D;
大鸟指导
-
A,B,C 命名不规范
-
写了四个判断分支,意味着每个条件都要做判断,等于计算机做了三次无用功
-
除法没考虑到除数为0的情况
小菜的第二次修改
<?php
try {
$numberA = $_POST['number_a'];
$operate = $_POST['operate'];
$numberB = $_POST['number_b'];
$result = '';
switch ($operate) {
case '+':
$result = round($numberA + $numberB, 2);
break;
case '-':
$result = round($numberA - $numberB, 2);
break;
case '*':
$result = round($numberA * $numberB, 2);
break;
case '/':
if ($numberB != 0) {
$result = round($numberA / $numberB, 2);
} else {
$result = '除数不能为0';
}
break;
}
echo '结果是: ' . $result;
} catch (Exception $e) {
echo '您的输入有误: ' . $e->getMessage();
}
大鸟指导(上)
这份代码实现计算器是没有问题了,但是这个代码是否符合面向对象呢?什么是面向对象?来听听这个故事
曹操吟诗的故事
话说三国时期,曹操带领百万大军攻打东吴,大军在长江赤壁驻扎,军船连成一片,眼看就要灭掉东吴,统一天下,曹操大悦,于是大宴众文武,在酒席间,曹操诗兴大发,不觉吟道: " 喝酒唱歌,人生真爽 … ". 众文武齐呼: “宰相好诗!” 于是一臣子速命刻板印刷,以便流传天下.
样张出来,曹操一看,觉得不妥,说到 “唱与喝,此话过俗,应改为’对酒当歌’较好!”,于是此臣就命工匠重新来过.工匠眼看连夜刻板之工,彻底白费.心中叫苦不迭,只得照办.
样张再次出来,曹操细细一品,觉得还是不好,说 "人生太爽太过直接,应改为问句才够意境,应改为’对酒当歌,人生几何?’ …"当臣转告工匠之时,工匠晕倒 …
问题出在哪里?
如果当时有活字印刷,而不是整个刻板重新刻,就不会有这些问题
活字印刷的优点
- 要改需要改动的字,此为可维护
- 未用到的字并非无用,完全可以在之后的印刷中重复使用,此为可复用
- 如果要加字,只需要另刻字加入即可,此为可扩展
- 字的排列可以横着可以竖着,只需要移动活字即可,此为灵活性好
大鸟指导(下)
你这份代码有哪些是和控制台无关,只和计算器有关的?把它们抽离出来,降低它的耦合度,便于维护或扩展
小菜的第三次修改
<?php
class Operation
{
public function getResult($numberA, $numberB, $operate)
{
$result = '';
switch ($operate) {
case '+':
$result = round($numberA + $numberB, 2);
break;
case '-':
$result = round($numberA - $numberB, 2);
break;
case '*':
$result = round($numberA * $numberB, 2);
break;
case '/':
if ($numberB != 0) {
$result = round($numberA / $numberB, 2);
} else {
$result = '除数不能为0';
}
break;
}
return $result;
}
}
try {
$numberA = $_POST['number_a'];
$operate = $_POST['operate'];
$numberB = $_POST['number_b'];
$result = (new Operation())->getResult($numberA, $numberB, $operate);
echo '结果是: ' . $result;
} catch (Exception $e) {
echo '您的输入有误: ' . $e->getMessage();
}
大鸟指导
仅此而已,还谈不上完全面向对象,这里仅用了面向对象三大特性之一[封装]
想想如何使用面向对象的继承和多态
- 如果让你新增一个开根(sqrt)运算,你如何改?
- 如果在switch加一个分支,意味着加减乘除的运算都得参与编译
- 如果你一不小心,改动了别的代码,岂不是凉凉?
小菜的第四次修改
<?php
class operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
$this->numberA = $numberA;
$this->numberB = $numberB;
}
public function getResult()
{
return 0;
}
}
class operationAdd extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
return round($this->numberA + $this->numberB, 2);
}
}
class operationSub extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
return round($this->numberA - $this->numberB, 2);
}
}
class operationNul extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
return round($this->numberA * $this->numberB, 2);
}
}
class operationDiv extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
if ($this->numberB == 0) {
throw new \Exception('除数不能为0');
}
return round($this->numberA / $this->numberB, 2);
}
}
大鸟指导
现在对面向对象运用的淋漓尽致了,下面学习下简单工厂模式
简单工厂模式
<?php
class operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
$this->numberA = $numberA;
$this->numberB = $numberB;
}
public function getResult()
{
return 0;
}
}
class operationAdd extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
return round($this->numberA + $this->numberB, 2);
}
}
class operationSub extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
return round($this->numberA - $this->numberB, 2);
}
}
class operationNul extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
return round($this->numberA * $this->numberB, 2);
}
}
class operationDiv extends Operation
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
parent::__construct($numberA, $numberB);
}
public function getResult()
{
if ($this->numberB == 0) {
throw new \Exception('除数不能为0');
}
return round($this->numberA / $this->numberB, 2);
}
}
class operationFactory
{
protected $numberA = 0;
protected $numberB = 0;
public function __construct($numberA, $numberB)
{
$this->numberA = $numberA;
$this->numberB = $numberB;
}
public function createOperate($operate)
{
$obj = null;
switch ($operate) {
case '+':
$obj = new operationAdd($this->numberA, $this->numberB);
break;
case '-':
$obj = new operationSub($this->numberA, $this->numberB);
break;
case '*':
$obj = new operationNul($this->numberA, $this->numberB);
break;
case '/':
$obj = new operationDiv($this->numberA, $this->numberB);
break;
}
return $obj;
}
}
//测试调用
$obj = new operationFactory(2, 3);
echo $obj->createOperate('*')->getResult();