2014-04-30 1 views
5

나는 공항 착륙 시스템을 모델링하는 프로젝트를 만들고있다. 난 planequeueplane 정렬하고 데이터베이스에 저장하는 데 필요한 모든 정보를 저장하는 개체가 있습니다. 모든 중요한 정보가 대상에 포함되어 있지만 각 비행기의 좌표도 포함 시켰습니다. 내 문제는 각각의 plane이 서로 다른 일을하기 때문에 응집력이있는 것으로 간주되지 않을 수도 있다는 것입니다.객체 응집력에 대한 Java 표준은 무엇입니까? 객체에서 너무 많은 정보가 나쁜 디자인을 의미합니까? - 예를 보자.

이것이 나쁜 디자인으로 간주되는지, 아니면 더 좋은 방법이 있는지 알고 싶습니다.

또한 개체의 응집력에 대한 "규칙"은 무엇입니까? 이걸 처리 할 수있는 특정 디자인 패턴이 있습니까?

public class Plane extends Aircraft { 
/* 
* Flight status should only take one of these enum values 
*/ 
private static enum Status { 
    REGISTERED, IN_QUEUE, LANDING, LANDED 
}; 

// Set aircraft status to REGISTERED when created 
private Status status = Status.REGISTERED; 

private double fuelLevelPercentage; 
private int passengerCount; 
private int aircraftNumber; 
private String airlineCompany; 
private String departureAirport; 

// This is used by the constructor to assign a random city to each new Aircraft 
private final String[] cities = { "Rome", "Berlin", "Heathrow", 
     "Edinburgh", "Cardiff", "Dublin", "Stansted" }; 
// Used to set airline companies 
private final String[] airLineCompanies = { "Easyjet", "Ryanair", 
     "British Airways","Flybe","Air Lingus", "Virgin" }; 


// Random number generator used by the constructor 
private Random rand; 

// Thread for this instance of Aircraft 
private Thread aircraftThread; 

// Radius of path when flying in circle (km?) 
private final double FLIGHT_RADIUS = 10; 
// Time taken to complete one complete loop (ms) 
private final double FLIGHT_PERIOD = 120000; 
// Angular frequency omega (rad/s) 
private double OMEGA = 2 * Math.PI/FLIGHT_PERIOD; 
// Time taken between being directed to land, and landing (ms) 
private int TIME_TAKEN_TO_LAND = 30000; 

// Time take to use one percent of fuel (ms) 
private double time_taken_to_use_one_percent_of_fuel = 30000; 

// variable to keep track of time since instantiated (ms) 
private int time = 0; 
// The aircraft Thread sleeps for TIME_STEP between updating 
private final int TIME_STEP = 20; 

private int time_when_called_to_land; 

private int hour_of_arrival; 
private int minute_of_arrival; 

/* 
* Set coordinates at time zero 
*/ 
private double x_coord = 0; 
private double y_coord = FLIGHT_RADIUS; 
private double altitude = 1000; 

/* 
* Used to calculate path to airport 
*/ 
private double x_coord_when_called; 
private double y_coord_when_called; 
private double altitude_when_called; 

Calendar calendar = Calendar.getInstance(); 

/** 
* This constructor sets the following fields to random values Dummy Data - 
* should have a better way to do this 
*/ 
public Plane() { 
    rand = new Random(); 

    this.fuelLevelPercentage = rand.nextInt(100); 
    this.departureAirport = cities[rand.nextInt(cities.length)]; 
    this.passengerCount = rand.nextInt(500); 
    this.aircraftNumber = rand.nextInt(50000000); 
    this.airlineCompany = airLineCompanies[rand.nextInt(airLineCompanies.length)]; 
} 

/** 
* this fly method will call on a different method depending on the status 
* of the Aircraft 
*/ 
public void fly() { 
    if (status == Status.REGISTERED) { 
     useFuel(); 
    } else if (status == Status.IN_QUEUE) { 
     flyInCircle(); 
     useFuel(); 
    } else if (status == Status.LANDING) { 
     flyToAirport(); 
     useFuel(); 
    } else if (status == Status.LANDED) { 

    } 
} 

public void flyInCircle() { 
    x_coord = FLIGHT_RADIUS * (Math.cos(OMEGA * (time))); 
    y_coord = FLIGHT_RADIUS * (Math.sin(OMEGA * (time))); 
} 

public void flyToAirport() { 
    if (!(x_coord < 1 && x_coord > -1 && y_coord < 1 && y_coord > -1 
      && altitude < 1 && altitude > -1)) { 
     x_coord -= x_coord_when_called * TIME_STEP/TIME_TAKEN_TO_LAND; 
     y_coord -= y_coord_when_called * TIME_STEP/TIME_TAKEN_TO_LAND; 
     altitude -= altitude_when_called * TIME_STEP/TIME_TAKEN_TO_LAND; 
    } else { 
     System.out.println("Aircraft landed"); 
     status = Status.LANDED; 
     hour_of_arrival = calendar.get(Calendar.HOUR_OF_DAY); 
     minute_of_arrival = calendar.get(Calendar.MINUTE); 
    } 

} 

/** 
* This method changes the flight status to IN_QUEUE - simulates telling the 
* plane to join queue 
*/ 
public void directToJoinQueue() { 
    setFlightStatus(Status.IN_QUEUE); 
} 

/** 
* This method changes the flight status to LANDING - simulates telling the 
* plane to land 
*/ 
public void directToflyToAirport() { 
    setFlightStatus(Status.LANDING); 
    time_when_called_to_land = time; 
    x_coord_when_called = x_coord; 
    y_coord_when_called = y_coord; 
    altitude_when_called = altitude; 
} 

/** 
* This method reduces fuel level according to fuel usage 
*/ 
private void useFuel() { 
    if (this.fuelLevelPercentage - TIME_STEP 
      /time_taken_to_use_one_percent_of_fuel > 0) { 
     this.fuelLevelPercentage -= TIME_STEP 
       /time_taken_to_use_one_percent_of_fuel; 
    } else { 
     this.fuelLevelPercentage = 0; 
    } 
} 

/** 
* this method sets the flight status 
*/ 
private void setFlightStatus(Status status) { 
    this.status = status; 
} 

public double getfuelLevelPercentage() { 
    return fuelLevelPercentage; 
} 

public int getPassengerCount() { 
    return passengerCount; 
} 

public void setPassengerCount(int passengerCount) { 
    this.passengerCount = passengerCount; 
} 

public int getAircraftNumber() { 
    return aircraftNumber; 
} 

public String getDepartureAirport() { 
    return departureAirport; 
} 

public void stop() { 
    ; 
} 

public String getAirlineCompany() { 
    return airlineCompany; 
} 

public void setAirlineCompany(String airlineCompany) { 
    this.airlineCompany = airlineCompany; 
} 

@Override 
public String toString() { 
    if (status == Status.LANDED) { 
     return String 
       .format("Flight %-8d | Fuel %-4.1f | Passengers %-3d | From %-10s | %-8s at %d:%d ", 
         aircraftNumber, fuelLevelPercentage, 
         passengerCount, departureAirport, status, 
         hour_of_arrival, minute_of_arrival); 
    } else { 
     return String 
       .format("Flight %-8d | Fuel %-4.1f | Passengers %-3d | From %-10s | %-8s | Coords (%-3.2f,%-3.2f) | Altitude %-4.2f", 
         aircraftNumber, fuelLevelPercentage, 
         passengerCount, departureAirport, status, x_coord, 
         y_coord, altitude); 
    } 

} 

public void start() { 
    aircraftThread = new Thread(this, this.getClass().getName()); 
    aircraftThread.start(); 
} 

@Override 
public void run() { 

    try { 

     while (true) { 
      calendar = Calendar.getInstance(); 
      fly(); 
      Thread.sleep(TIME_STEP); 
      time += TIME_STEP; 

     } 

     // System.out.println("aircraft number "+aircraftNumber+" safely landed"); 

    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

} 
} 

답변

5

응집력은 어려운 개념이다. 다른 답변의 경박 한 응답에도 불구하고 진정한 대답은 시스템이하는 일과 작동 방식에 달려 있습니다. 예를 들어 큐 메커니즘을 검사 할 수 있습니다. 당신의 시스템에서, 비행기가 대기열에있을 때 명령에 다르게 반응합니까? 그렇다면 대기열에 있다는 사실이 비행기에 필수적이어야합니다. 다른 대기열에있을 때 다르게 반응합니까? 그렇다면 대기열 자체가 평면에 필수적이어야합니다. 그러나 비행기가 대기열에 있기 때문에 다르게 반응하는 공항이라면 공항이 대기열을 제어해야하며 비행기가 그것에 대해 알지 않아야합니다. 공항 (또는 공항)이 비행 경로를 제공해야합니다. 모델의 해상도에 따라 공항의 제어 탑).

여기에는 응집력 만이 유일한 문제는 아닙니다. 캡슐화 또한 큰 문제입니다. 다른 객체가 내부 상태에 액세스하도록 허용하고 있습니다. 이것을 완전히 OO 방식으로 모델링하려면 CQRS 패턴의 사용을 고려해야합니다. DDD (Domain Driven Design) 기술을 고려하고 경계가있는 컨텍스트를 식별하고 경로를 집계하는 것으로 시작하면 올바른 디자인을 더 많이 유추 할 수 있습니다.

+0

"Flippant"? 나는 여기에 경박 한 것을 본다. – duffymo

+1

@duffymo - 열심히 가져 가지 마십시오. 나는 OP에 자신에게 대답하는 방법을 가르치지 않고 질문에 대답했다고 생각합니다. 나는 당신의 대답이 잘못되었다고 생각하지 않지만, 나는 그것이 매우 도움이되었다고 생각하지 않는다. –

+0

힘들게 복용하지 마십시오. 나는 내가 가르친 것을 보여주기 위해 코드를 게시했다. 나는 Eric Evans를 인용하지 않았을 지 모르지만 큐잉을 분리하라는 권고는 나에게서 먼저왔다. – duffymo

4

Java 또는 다른 언어에는 '표준'이 없습니다.

은 내가 대기열에 종류에 비행기를 필요로하고 데이터베이스에 전달하는 모든 정보를 저장하는 "면"개체가 있습니다. 중요한 모든 정보가 개체에 포함되어 있지만 각 비행기의 좌표는 입니다.

여러분의 평면 모델 객체가 너무 많이하고 있다고 생각합니다.

대기열에 있는지 여부를 알아야하는 이유가 없습니다. 큐를 소유 한 별도의 개체를 규칙을 알고 있습니다.

메모리 내 콜렉션 또는 메시지 대기열을 대기열에 넣었습니까? 너에게 중요한거야?

모델 개체는 계속 논쟁의 대상입니다. 퍼시스턴스를 별도의 데이터 액세스 개체로 분리하는 것이 더 쉽기 때문에 테스트하기가 더 쉽습니다.

귀하의 모델은 다음과 같습니다

package model; 

public class Plane { 

    private int id; 
    public void save() { 
     // persist the state of this 
     // INSERT INTO PLANE(id) VALUES(?) 
    } 
} 

내가 별도의 패키지에서 DAO 인터페이스이있을 것이다 :

package persistence; 

public interface PlaneDAO { 
    void save(Plane p); 
} 
+0

개체가 대기열에 있는지 여부를 보지 않아야한다는 것에 동의합니다. 나는 그것을 바꿀 것이다. 객체 자체가 지속된다는 것은 무엇을 의미합니까? 지속성을 분리함으로써? 감사합니다 – Zanarou

1

개체 응집력에 관한 Java 표준이 없습니다.(나는 두피모의 충고를 반복하지 않는다. 나는 그들 모두에 동의한다.) 당신은 현실 세계를 매핑하는 객체 모델을 정교 때 명심해야 할

것은 실제의 하나의 클래스 매핑 하나의 개념이하는 것입니다.

예를 들어, 예제 코드에는 PlaneFlight의 두 가지 고유 개념이 있으므로 두 인스턴스를 일대 다 관계로 두 개의 별도 클래스로 나눌 수 있습니다.

2

응집도는 degree to which the elements of a module belong together으로 정의 할 수 있습니다.

시각화가 도움이됩니다. 클래스의 속성과 메소드를 상상해보십시오. 클래스가 응집력이 있다면 메서드가 많은 특성을 사용한다는 것을 의미하고 반대로 특성은 여러 메서드에서 사용됩니다. 이것은 응집력의 "서로 붙어있는"개념입니다. 다른 사람들이 지적

enter image description here

는, 비행기를 직접 방법 (예, directToX이) 어떤 비행기가의 "테마"이외의 가능성이 있습니다 : 나는 NDepend's placemat에서 오는 다음 시각화를 좋아한다 , 그러나 그들은 틀림없이 그릇된 것이 아닙니다. 이러한 요소 (책임)는 다른 클래스에서 더 좋을 수 있습니다 (예 : AirTrafficController). 실제로 비행기는 비행 방법을 많이 결정하지 않습니다. 그들의 조종사는지면에서 지시를 따라야합니다.

나는 Thread stuff (start, run)이 확실히 Plane의 테마 밖에 있다고 주장 할 것이다. 그 방법은 거의 Plane의 일부인 것을 사용하지 않습니다 (그들은 이라는 주제로입니다). main에서 use an anonymous inner class to handle the processing in a thread 수 있으며 귀하의 비행기가 훨씬 더 재사용 (및 응집력) 것입니다.

응축 물은 본질의 모델에 도착합니다. 즉, 다른 응용 프로그램 (또는 다른 OO 언어)에서 쉽게 다시 사용할 수 있습니다. 개념의 진정한 주제를 벗어나기 시작하는 것은 다른 용도로 개념을 재사용하는 것을 어렵게 만듭니다. "주의 산만"은 다른 응용 프로그램에서 더 이상 이해가되지 않습니다.

Kamikaze 프로젝트 (방금 만든 것이고 재사용하지 않으려 고하는 프로젝트)를 개발중인 경우 응집성 및 기타 디자인 요소를 잊어도 무방합니다. 디자인 선택은 절충점입니다. Plane 클래스를 리팩터링하여보다 응집력있게 만들 수 있지만 다른 응용 프로그램에서 결코 다시 사용하지 않으면 아마도 시간을 낭비했을 것입니다. 반면 디자인은 학습 과정입니다. 한 응용 프로그램에 대해 무언가를 과도하게 설계하더라도 다음 번에 뭔가를 배웠을 것입니다.

마지막으로 모든 디자인 측면을 정량화하기가 쉽지 않으므로 표준이 거의 없습니다. 일부 기업은 개발 프로세스에서 LCOM과 같은 메트릭에 대해 (임의의) 표준을 설정하는 것으로 알려져 있습니다. 나는 클래스가 LCOM에 나쁜 가치를 지닌다면, 그 값이 충분히 낮아질 때까지 리팩토링되어야한다고 말한 팀 표준을 읽었습니다 (그 응집성이 강합니다). 죄송합니다. LCOM can be a bad measure of cohesion (especially in classes that have lots of get/set methods).

관련 문제