пятница, октября 07, 2016

If-less programming (java) Part №2

В предыдущей части мы рассмотрели как избавиться от if в паттерне "фабричный метод". Но есть паттерны специально придуманные чтобы избавить вас от этой проблемы. Один из них паттерн "Состояние".


Рассмотрим небольшой пример. У нас есть класс Tractor, у которого есть состояние: Ориентация по стороне света и координаты. У трактора есть возможность повернуться(метод turn) и двигаться в этом направлении (метод move).
package com.bssys.blog;

public class Tractor {

 public Orientation currentOrientation;
 public Coordinate currentCoordinate;

 public Tractor() {
  currentOrientation = Orientation.North;
  currentCoordinate = new Coordinate(0, 0);
 }

 public Tractor(Orientation currentOrientation, Coordinate currentCoordinate) {
  this.currentOrientation = currentOrientation;
  this.currentCoordinate = currentCoordinate;
 }

 public void move() {

  if (currentOrientation == Orientation.North) {
   currentCoordinate.y = currentCoordinate.y + 1;
  } else if (currentOrientation == Orientation.South) {
   currentCoordinate.y = currentCoordinate.y - 1;
  } else if (currentOrientation == Orientation.West) {
   currentCoordinate.x = currentCoordinate.x - 1;
  } else if (currentOrientation == Orientation.East) {
   currentCoordinate.x = currentCoordinate.x + 1;
  }
 }

 public void turn() {
  if (currentOrientation == Orientation.North) {
   currentOrientation = Orientation.East;
  } else if (currentOrientation == Orientation.South) {
   currentOrientation = Orientation.West;
  } else if (currentOrientation == Orientation.West) {
   currentOrientation = Orientation.North;
  } else if (currentOrientation == Orientation.East) {
   currentOrientation = Orientation.South;
  }
 }

 public Orientation getCurrentOrientation() {
  return currentOrientation;
 }

 public Coordinate getCurrentCoordinate() {
  return currentCoordinate;
 }

 @Override
 public String toString() {
  return "" + currentOrientation + " " + currentCoordinate;
 }
}

class Coordinate {

 public int x;
 public int y;

 public Coordinate(int x, int y) {
  this.x = x;
  this.y = y;
 }

 @Override
 public String toString() {
  return "x=" + x + " " + "y=" + y;
 }
}

enum Orientation {
 North, South, West, East
}

Обычно решение в лоб вырождается в портянку if-else в зависимости от некоторого состояния. В нашем случае состояние представляет собой направление движения, т.е. ориентацию по стороне света. Для того чтобы решить эту проблему выделим интерфейс состояния. Он будет представлять собой следующее:

interface State {
 public void turn(Tractor tractor);
 public void move();
}
Реализуем этот интерфейс для всех наших возможных состояний, и добавим в класс Tractor поле хранящее текущее состояние согласно нашему паттерну:
class EastState implements State {
 
 private Coordinate coordinate;
 
 public EastState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new SouthState(coordinate));
 }
 
 public void move() {
  coordinate.x = coordinate.x + 1;
 }

 @Override
 public String toString() {
  return "East:" + coordinate;
 }
}
class NorthState implements State {
 
 private Coordinate coordinate;
 
 public NorthState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new EastState(coordinate));
 }
 
 public void move() {
  coordinate.y = coordinate.y + 1;
 }

 @Override
 public String toString() {
  return "North:" + coordinate;
 }
}
class SouthState implements State {
 
 private Coordinate coordinate;
 
 public SouthState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new WestState(coordinate));
 }

 public void move() {
  coordinate.y = coordinate.y - 1;
 }

 @Override
 public String toString() {
  return "South:" + coordinate;
 }
}
class WestState implements State {
 
 private Coordinate coordinate;
 
 public WestState(Coordinate coordinate) {
  this.coordinate = coordinate;
 }

 public void turn(Tractor tractor) {
  tractor.setCurrentState(new NorthState(coordinate));
 }

 public void move() {
  coordinate.x = coordinate.x - 1;
 }

 @Override
 public String toString() {
  return "West:" + coordinate;
 }
}

Смотрите, логика переходов по состояниям у нас теперь находится в специальных классах. И класс Tractor теперь просто должен делегировать свои действия текущему состоянию. Обратите внимание, что меняет состояние только метод turn. Таким образом итоговый код класса Tractor вырождается в следующее:
public class Tractor {

 private State currentState;

 public Tractor() {
  currentState = new NorthState(new Coordinate(0, 0));
 }

 public void move() {
  currentState.move();
 }

 public void turn() {
  currentState.turn(this);
 }

 public void setCurrentState(State state) {
  this.currentState = state;
 }

 @Override
 public String toString() {
  return currentState.toString();
 }

 public static void main(String[] args) {
  Tractor tractor = new Tractor();
  System.out.println(tractor);
  tractor.turn();
  tractor.move();
  tractor.move();
  tractor.move();
  System.out.println(tractor);
  tractor.turn();
  tractor.move();
  tractor.move();
  tractor.move();
  System.out.println(tractor);
 }
}

1 комментарий:

hannekage комментирует...

Casino City - MapYRO
Casino City. Casino 문경 출장마사지 City. 양산 출장샵 Mapyro® 시흥 출장마사지 is 순천 출장안마 a fun and friendly community place. Find a table game floor, live music and free WiFi 수원 출장마사지 in rooms and suites.