写点什么

对进入面向对象世界的思考

用户头像
云飞
关注
发布于: 2020 年 06 月 16 日
对进入面向对象世界的思考

练习题

刚开始学习有关UML和面向对象中的设计原则,于是找了一道题来试一试

最开始我们设计的类图是如下所示:



public class Button{
public final static int SEND_BUTTON = -99;
private Dialer dialer;
private int token;
public Button(int token, Dialer dialer){
this.token = token;
this.dialer = dialer;
}
public void press(){
switch(token){
case 0: case 1: case 2: case 3: case 4:
case 5: case 6: case 7: case 8: case 9:
dialer.entreDigit(token);
break;
case SEND_BUTTON:
dialer.dial();
break;
default:
thor new UnsupportedOperationException("unknow button pressed: token=" + token);
}
}
}



public class Dialer{
public void enterDigit(int digit){
scree.display(digit);
speaker.beep(digit);
}
public void dial(){
screen.display("dialing...");
radio.connect();
}
}

带来的问题

然而对上面的 Button/Dialer 类,会有几个问题如僵硬、脆弱、不可移植

  • 僵硬

  • 增加一种 Button 类型,就需要对 Button 类进行修改;

  • 修改 Dialer,可能回影响 Button

  • 脆弱

  • 当想修改 Send 按钮功能时,有可能不小心破坏数字按钮;

  • 当这种函数很多时,很可能会漏掉某个函数,或其中的某个条件分支。

  • 不可移植

  • 设想我们要设计密码锁的按钮,它只需要数字按钮,但 Button 的设计使它必须“附带”一个“send”类型的按钮


改进方法一:

对于以上的问题它不满足开闭原则,因此需要对齐进行改进

第一种改进方法,使用继承的方式

因为我又不甘只看图,所以也顺手写了一下代码,具体代码如下:

Button类:

public class Button {
public void press(){}
}

DigitButton类:

public class DigitButton extends Button {
private int token;
Dialer dialer;
public DigitButton (int token,Dialer dialer) {
this. token = token;
this.dialer = dialer ;
}
@Override
public void press() {
dialer.enterDigit(token);
}
}

SendButton类:

public class SendButton extends Button {
Dialer dialer;
public SendButton (Dialer dialer) {
this.dialer = dialer ;
}
@Override
public void press() {
super.press();
dialer.dial();
}
}

Dialer类:

public class Dialer {
public void enterDigit (int digit) {
System.out.println("展示这个数字"+digit);
System.out.println("声音"+digit);
}
public void dial () {
System.out.println("展示输入数字串");
System.out.println("无限传播连接");
}
}




改进方法二:

改进方法二使用策略模式:

自己也是第一次看着UML来写代码,第一次的我写成了这个样子,不怕大家笑话我就直接上图了。

刚写完我就马上意识到了自己写的有些问题

  • 从图里可以看到 Button 类根本没有 跟 ButtonServer 之间产生依赖关系,也就是上图中虚线的箭头

  • 对于第二个向上指向的箭头,应该是继承的关系,我默认 ButtonServer 是一个类了,但如果要符合依赖导致原则,高层模块依赖接口而不应该依赖实现,所以 ButtonServer 应该是一个接口,然后 Dailer 类实现 ButtonServer

  • 已经意识到这写的啥也不是,根本不是一个策略模式,于是又重新看了策略模式的类图 如下图所示



然后进行了第二次修改

Button类:

public class Button {
private int token;
private ButtonServer buttonServer;
public Button(int token, ButtonServer buttonServer) {
this.token = token;
this.buttonServer = buttonServer;
}
public void press(){
buttonServer.buttonPress(token);
}
}

ButtonServer类:

public interface ButtonServer {
public void buttonPress(int token) ;
}

Dialer类:

public class Dialer implements ButtonServer {
List<Integer> buttonList = new LinkedList<>();
public void enterDigit(int digit) {
System.out.println("展示这个数字"+digit);
System.out.println("声音"+digit);
}
public void dial() {
for (Integer token : buttonList) {
System.out.print(token);
}
System.out.println("无限传播 connect");
}
@Override
public void buttonPress(int token) {
if(token > 0 && token < 9){
buttonList.add(token);
enterDigit(token);
}
if(token == 99){
dial();
}
}
}

Main:

public class Main {
public static void main(String[] args) {
ButtonServer dialer = new Dialer();//这是一种策略
Button button1 = new Button(1, dialer);
Button button2 = new Button(2, dialer);
Button button3 = new Button(3, dialer);
Button sendButton = new Button(99, dialer);
button1.press();
button2.press();
button3.press();
sendButton.press();
}
}




改进方法三:

改进方法三:适配器模式



来继续分享一下自己第一次照图写出代码的图,跟上一个一样,大概就看到了有几个类,类里面的方法

说一下上面的问题

  • Button跟ButtonServer应该是依赖关系,在我的第一个类里面什么都没有,没有之间的调用关系这里的问题最严重

  • 然后还是 Button 其实这里应该是 策略模式,我这里些的啥也不是,这里只有适配器写对了

然后我们看正确答案

Button 类调用 ButtonServer

public class Button {
ButtonServer buttonServer;
public Button(ButtonServer dialer) {
this.buttonServer = dialer;
}
public void press(){
buttonServer.buttonPress();
}
}

ButtonServer 类

public interface ButtonServer {
public void buttonPress();
}

DigitButtonDialerAdapter类

public class DigitButtonDialerAdapter implements ButtonServer{
private int token;
private Dialer dialer;
public DigitButtonDialerAdapter(int token,Dialer dialer) {
this.token = token;
this.dialer = dialer;
}
public Dialer getDialer() {
return dialer;
}
public void setDialer(Dialer dialer) {
this.dialer = dialer;
}
@Override
public void buttonPress() {
dialer.enterDigit(token);
}
}

SendButtonDialerAdapter类

public class SendButtonDialerAdapter implements ButtonServer{
private Dialer dialer;
public SendButtonDialerAdapter(Dialer dialer) {
this.dialer = dialer;
}
public Dialer getDialer() {
return dialer;
}
public void setDialer(Dialer dialer) {
this.dialer = dialer;
}
@Override
public void buttonPress() {
dialer.dial();
}
}

Main方法

public class Main {
public static void main(String[] args) {
Dialer dialer = new Dialer();
Button button1 = new Button(new DigitButtonDialerAdapter(1,dialer));
Button button2 = new Button(new DigitButtonDialerAdapter(2,dialer));
Button button3 = new Button(new DigitButtonDialerAdapter(3,dialer));
Button sendButton = new Button(new SendButtonDialerAdapter(dialer));
button1.press();
button2.press();
button3.press();
sendButton.press();
}
}




改进方法四:

方法四:观察者模式



Button类

public class Button {
private List<ButtonListener> listeners;
public Button() {
this.listeners = new LinkedList<ButtonListener>();
}
public void addListener(ButtonListener listener) {
assert listener != null;
listeners.add(listener);
}
public void press(){
for (ButtonListener buttonListener : listeners) {
buttonListener.buttonPressed();
}
}
}

ButtonListener类

public interface ButtonListener {
public void buttonPressed();
}

Dialer类

public class Dialer {
public void enterDigit (int digit) {
System.out.println("展示这个数字"+digit);
System.out.println("声音"+digit);
}
public void dial () {
System.out.println("展示输入数字串");
System.out.println("无限传播连接");
}
}

Phone类

public class Phone {
private Dialer dialer;
private Button[] digitButtons;
private Button sendButton;
public Phone(){
dialer = new Dialer();
digitButtons = new Button[10];
for(int i = 0;i < digitButtons.length;i++){
digitButtons[i] = new Button();
final int digit = i;
digitButtons[i].addListener(new ButtonListener() {
@Override
public void buttonPressed() {
dialer.enterDigit(digit);
}
});
}
sendButton = new Button();
sendButton.addListener(new ButtonListener() {
@Override
public void buttonPressed() {
dialer.dial();
}
});
}
public static void main(String[] args) {
Phone phone = new Phone();
phone.digitButtons[9].press();
phone.digitButtons[1].press();
phone.digitButtons[1].press();
phone.sendButton.press();
}
}



总结:在这次Demo的练习当中感觉自己已经踏入了面向对象编程的世界,已经体会到了面向对象带来的红利,利用多态的特性来帮助我们实现真正的敏捷开发,最后感谢李智慧老师给予的指导,谢谢老师。

发布于: 2020 年 06 月 16 日阅读数: 529
用户头像

云飞

关注

还未添加个人签名 2018.09.29 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
策略模式,if判断应该带等号:if(token >= 0 && token <= 9)
2020 年 09 月 30 日 19:58
回复
没有更多了
对进入面向对象世界的思考