programing

'상수'를 공유하기 위한 Java 정적 필드가 있는 인터페이스

itsource 2022. 10. 25. 22:02
반응형

'상수'를 공유하기 위한 Java 정적 필드가 있는 인터페이스

Java에 들어가기 위한 오픈 소스 Java 프로젝트를 살펴보고 있는데, 많은 프로젝트가 일종의 '일정' 인터페이스를 가지고 있다는 것을 알 수 있습니다.

예를 들어 processing.org에는 PConstants.java라는 이름의 인터페이스가 있으며 다른 대부분의 코어 클래스는 이 인터페이스를 구현합니다.인터페이스는 스태틱멤버로 가득 차 있어요이 접근법에 이유가 있나요?아니면 이것이 잘못된 관행으로 간주됩니까?타당할 때 enum을 사용하거나 정적 클래스를 사용하면 어떨까요?

나는 일종의 유사 '글로벌 변수'를 허용하기 위해 인터페이스를 사용하는 것이 이상하다고 생각한다.

public interface PConstants {

  // LOTS OF static fields...

  static public final int SHINE = 31;

  // emissive (by default kept black)
  static public final int ER = 32;
  static public final int EG = 33;
  static public final int EB = 34;

  // has this vertex been lit yet
  static public final int BEEN_LIT = 35;

  static public final int VERTEX_FIELD_COUNT = 36;


  // renderers known to processing.core

  static final String P2D    = "processing.core.PGraphics2D";
  static final String P3D    = "processing.core.PGraphics3D";
  static final String JAVA2D = "processing.core.PGraphicsJava2D";
  static final String OPENGL = "processing.opengl.PGraphicsOpenGL";
  static final String PDF    = "processing.pdf.PGraphicsPDF";
  static final String DXF    = "processing.dxf.RawDXF";


  // platform IDs for PApplet.platform

  static final int OTHER   = 0;
  static final int WINDOWS = 1;
  static final int MACOSX  = 2;
  static final int LINUX   = 3;

  static final String[] platformNames = {
    "other", "windows", "macosx", "linux"
  };

  // and on and on

}

그것은 일반적으로 나쁜 관행으로 여겨진다.문제는 상수가 구현 클래스의 퍼블릭 "인터페이스"의 일부라는 것입니다(더 나은 단어가 필요하기 때문입니다).즉, 구현 클래스는 내부에서만 필요한 경우에도 이러한 값을 모두 외부 클래스에 퍼블리시하고 있음을 의미합니다.상수는 코드 전체에 걸쳐 증식합니다.예를 들어 Swing의 SwingConstants 인터페이스는 모든 상수(사용하지 않는 상수까지 포함)를 자체 "재export"하는 수십 개의 클래스에 의해 구현됩니다.

하지만 제 말을 믿지는 마세요. 조쉬 블로흐도 나쁘다고 말합니다.

인터페이스 패턴이 일정하면 인터페이스가 제대로 사용되지 않습니다.클래스가 내부적으로 일부 상수를 사용하는 것은 구현 세부 사항입니다.지속적인 인터페이스를 구현하면 이 구현 세부 정보가 클래스의 내보낸 API로 유출됩니다.클래스가 일정한 인터페이스를 구현하는 것은 클래스 사용자에게 중요하지 않습니다.사실, 그것은 그들을 혼란스럽게 할 수도 있다.더 나쁜 것은 이것이 약속임을 나타냅니다.앞으로 출시될 릴리스에서 클래스가 상수를 사용할 필요가 없도록 변경된 경우에도 바이너리 호환성을 확보하기 위해 인터페이스를 구현해야 합니다.비최종 클래스가 일정한 인터페이스를 실장하고 있는 경우, 그 서브 클래스의 모든 네임스페이스는 인터페이스의 상수에 의해 오염됩니다.

열거형이 더 나은 접근법일 수 있습니다.또는 단순히 인스턴스화할 수 없는 클래스의 퍼블릭 스태틱필드로 상수를 넣을 수도 있습니다.이를 통해 다른 클래스는 자신의 API를 오염시키지 않고 해당 클래스에 액세스할 수 있습니다.

Java 1.5+에서는 "정수 인터페이스"를 구현하는 대신 정적 가져오기를 사용하여 다른 클래스/인터페이스에서 상수/정적 메서드를 가져올 수 있습니다.

import static com.kittens.kittenpolisher.KittenConstants.*;

이것에 의해, 클래스에 기능이 없는 인터페이스를 실장하는 추악한 일이 없어집니다.

상수를 저장하기 위해서만 수업을 하는 연습에 대해서는 때때로 필요하다고 생각합니다.클래스에 자연스러운 위치가 없는 특정 상수가 있으므로 "중립" 위치에 두는 것이 좋습니다.

그러나 인터페이스를 사용하는 대신 개인 생성자와 함께 최종 클래스를 사용하십시오(클래스를 인스턴스화하거나 하위 클래스로 만들 수 없도록 하고, 비정적 기능/데이터를 포함하지 않는다는 강력한 메시지를 보냅니다).

예:

/** Set of constants needed for Kitten Polisher. */
public final class KittenConstants
{
    private KittenConstants() {}

    public static final String KITTEN_SOUND = "meow";
    public static final double KITTEN_CUTENESS_FACTOR = 1;
}

나는 옳은 척 하는 것이 아니라, 다음의 작은 예를 보자.

public interface CarConstants {

      static final String ENGINE = "mechanical";
      static final String WHEEL  = "round";
      // ...

}

public interface ToyotaCar extends CarConstants //, ICar, ... {
      void produce();
}

public interface FordCar extends CarConstants //, ICar, ... {
      void produce();
}

// and this is implementation #1
public class CamryCar implements ToyotaCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

// and this is implementation #2
public class MustangCar implements FordCar {

      public void produce() {
           System.out.println("the engine is " + ENGINE );
           System.out.println("the wheel is " + WHEEL);
      }
}

도요타차는 포드카에 대해 아무것도 모르고 포드카도 도요타차에 대해 모른다.카콘스턴트의 원칙은 바뀌어야 하지만...

휠이 둥글고 직선은 기계적이기 때문에 상수는 변경하지 마십시오. 하지만...미래에는 도요타의 연구 기술자들이 전기 엔진과 평평한 바퀴를 발명했다!새로운 인터페이스를 표시하다

public interface InnovativeCarConstants {

          static final String ENGINE = "electronic";
          static final String WHEEL  = "flat";
          // ...
}

이제 추상화를 바꿀 수 있습니다.

public interface ToyotaCar extends CarConstants

로.

public interface ToyotaCar extends InnovativeCarConstants 

또한 핵심 값을 변경해야 할 경우 엔진 또는 휠의 경우 구현에 영향을 주지 않고 추상화 수준에서 도요타 자동차 인터페이스를 변경할 수 있습니다.

안전하지 않다는 건 알지만 그래도 알고 싶어요

자바에서는 이 패턴에 대한 혐오가 많다.단, 정적 상수의 인터페이스가 값을 가질 수 있습니다.기본적으로 다음 조건을 충족해야 합니다.

  1. 이러한 개념은 여러 클래스의 퍼블릭인터페이스의 일부입니다.

  2. 이러한 값은 향후 릴리스에서 변경될 수 있습니다.

  3. 모든 구현에서 동일한 값을 사용하는 것이 중요합니다.

예를 들어, 가상 쿼리 언어에 대한 확장을 쓰고 있다고 가정합니다.이 확장에서는 인덱스에서 지원되는 몇 가지 새로운 작업을 사용하여 언어 구문을 확장합니다. 예를 들어 지리 공간 쿼리를 지원하는 R-Tree가 있습니다.

따라서 퍼블릭인터페이스를 스태틱 상수로 씁니다.

public interface SyntaxExtensions {
     // query type
     String NEAR_TO_QUERY = "nearTo";

     // params for query
     String POINT = "coordinate";
     String DISTANCE_KM = "distanceInKm";
}

나중에 새로운 개발자가 더 나은 인덱스를 구축해야 한다고 생각하여 R* 구현을 구축합니다.이 인터페이스를 새로운 트리에 구현함으로써 다른 인덱스가 쿼리 언어로 동일한 구문을 가질 수 있습니다.또한 나중에 "near To"가 혼란스러운 이름이라고 판단한 경우 "within DistanceInKm"로 변경하여 모든 인덱스 구현에서 새로운 구문을 사용할 수 있습니다.

PS: 이 예제의 영감은 Neo4j 공간 코드에서 가져온 것입니다.

나중에 생각해보면 자바가 여러모로 망가져 있다는 것을 알 수 있다.Java의 주요 장애 중 하나는 인터페이스가 추상 메서드와 정적 최종 필드로 제한된다는 것입니다.Scala와 같은 새롭고 더 정교한 OO 언어들은 구체적인 방법을 포함할 수 있는 특성(일반적으로 그러함)에 의해 인터페이스를 잠재합니다. 이 특성들은 arity 0(상수!)을 가질 수 있습니다.구성 가능한 동작의 단위로서의 특성에 대한 설명은 http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf를 참조하십시오.Scala 의 특성과 Java 의 인터페이스의 비교에 대해서는, http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-5 를 참조해 주세요.OO디자인을 가르칠 때 인터페이스에 정적 필드가 포함되어서는 안 된다고 주장하는 것과 같은 단순한 규칙은 어리석습니다.많은 특성은 자연스럽게 상수를 포함하며 이러한 상수는 특성에 의해 지원되는 공공 "인터페이스"의 일부입니다.Java 코드를 기술할 때는 특성을 나타내는 깨끗하고 우아한 방법은 없지만 인터페이스 내에서 정적 최종 필드를 사용하는 것이 좋은 회피책의 일부입니다.

JVM 사양에 따르면 인터페이스의 필드 및 메서드는 Public, Static, Final 및 Abstract만 포함할 수 있습니다.내부 Java VM 참조

기본적으로는 인터페이스의 모든 메서드는 추상적이며 명시적으로 언급하지 않아도 어렵습니다.

인터페이스는 사양만 제시하도록 되어 있습니다.실장은 포함할 수 없습니다.따라서 사양 변경을 위한 클래스 구현을 피하기 위해 최종 사양으로 합니다.인터페이스를 인스턴스화할 수 없기 때문에 인터페이스 이름을 사용하여 필드에 액세스하기 위해 스태틱하게 됩니다.

저는 Pleerock에게 코멘트를 할 정도의 평판이 없기 때문에 답변을 작성해야 합니다.죄송합니다만, 그는 열심히 노력하고 있기 때문에 답변을 드리고 싶습니다.

Pleerock: 이러한 상수가 인터페이스로부터 독립되어야 하고 상속으로부터 독립되어야 하는 이유를 보여 주는 완벽한 예를 만들었습니다.애플리케이션의 고객에게 있어서, 자동차의 실장 사이에 기술적인 차이가 있는 것은 중요하지 않다.고객도 마찬가지고, 차만 있습니다.따라서 클라이언트는 I_Somecar와 같은 인터페이스라는 관점에서 이 인터페이스를 검토하고자 합니다.고객은 어플리케이션 전체를 통해 각 자동차 브랜드별로 다른 관점을 사용하지 않고 하나의 관점만을 사용합니다.

고객이 구입하기 전에 자동차를 비교하고 싶다면 다음과 같은 방법을 사용할 수 있습니다.

public List<Decision> compareCars(List<I_Somecar> pCars);

인터페이스는 동작에 관한 계약으로 하나의 관점에서 다른 오브젝트를 보여줍니다.당신이 디자인하는 방식대로 모든 자동차 브랜드가 고유의 유산 계통을 가질 수 있을까요?비록 현실적으로는 꽤 맞지만, 자동차는 완전히 다른 종류의 물체를 비교하는 것과 같을 수 있기 때문에, 결국 다른 차들 사이에 선택의 여지가 있다.이것이 모든 브랜드가 공유해야 하는 인터페이스의 관점입니다.상수의 선택으로 인해 이것이 불가능하게 되어서는 안 됩니다.자코넨의 대답을 생각해 보세요

이는 Java 1.5가 존재하기 이전부터 시작되었으며 Enum을 제공합니다.그 이전에는 일련의 상수나 제약된 값을 정의할 수 있는 좋은 방법이 없었습니다.

대부분의 경우 이전 버전과의 호환성을 위해 또는 제거에 필요한 리팩터링을 위해 많은 프로젝트에서 여전히 사용되고 있습니다.

언급URL : https://stackoverflow.com/questions/320588/interfaces-with-static-fields-in-java-for-sharing-constants

반응형