ฉันเพิ่งพบว่า Java อนุญาตให้ enums ใช้งานอินเตอร์เฟส สิ่งที่จะเป็นกรณีใช้งานที่ดีสำหรับการที่?
ฉันเพิ่งพบว่า Java อนุญาตให้ enums ใช้งานอินเตอร์เฟส สิ่งที่จะเป็นกรณีใช้งานที่ดีสำหรับการที่?
คำตอบ:
Enums ไม่จำเป็นต้องเป็นตัวแทนของเซตแบบพาสซีฟ (เช่นสี) พวกเขาสามารถแสดงวัตถุที่ซับซ้อนมากขึ้นกับการทำงานและเพื่อให้คุณจากนั้นก็มีแนวโน้มที่จะต้องการที่จะเพิ่มฟังก์ชันการทำงานต่อไปเหล่านี้ - เช่นคุณอาจจะมีการเชื่อมต่อเช่นPrintable
, Reportable
ฯลฯ และส่วนประกอบที่สนับสนุนเหล่านี้
นี่คือตัวอย่างหนึ่ง (พบ / คล้ายกันที่ดีกว่าใน Effective Java 2nd Edition):
public interface Operator {
int apply (int a, int b);
}
public enum SimpleOperators implements Operator {
PLUS {
int apply(int a, int b) { return a + b; }
},
MINUS {
int apply(int a, int b) { return a - b; }
};
}
public enum ComplexOperators implements Operator {
// can't think of an example right now :-/
}
ตอนนี้เพื่อรับรายชื่อทั้ง Simple + Complex Operators:
List<Operator> operators = new ArrayList<Operator>();
operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));
ดังนั้นที่นี่คุณจะใช้อินเทอร์เฟซเพื่อจำลอง enums ที่ขยายได้ (ซึ่งไม่สามารถทำได้โดยไม่ต้องใช้อินเตอร์เฟส)
Comparable
ตัวอย่างที่ได้รับจากคนหลายคนที่นี่เป็นสิ่งที่ผิดตั้งแต่Enum
อยู่แล้วว่าการดำเนินการ คุณไม่สามารถแทนที่มันได้
ตัวอย่างที่ดีกว่าคือมีอินเทอร์เฟซที่กำหนดสมมติว่าเป็นชนิดข้อมูล คุณสามารถมี enum เพื่อใช้ชนิดง่าย ๆ และมีคลาสปกติเพื่อใช้ชนิดที่ซับซ้อน:
interface DataType {
// methods here
}
enum SimpleDataType implements DataType {
INTEGER, STRING;
// implement methods
}
class IdentifierDataType implements DataType {
// implement interface and maybe add more specific methods
}
มีบางกรณีที่ฉันมักจะใช้ ฉันมีIdUtil
ชั้นเรียนที่มีวิธีคงที่ในการทำงานกับวัตถุที่ใช้Identifiable
อินเตอร์เฟซที่เรียบง่ายมาก:
public interface Identifiable<K> {
K getId();
}
public abstract class IdUtil {
public static <T extends Enum<T> & Identifiable<S>, S> T get(Class<T> type, S id) {
for (T t : type.getEnumConstants()) {
if (Util.equals(t.getId(), id)) {
return t;
}
}
return null;
}
public static <T extends Enum<T> & Identifiable<S>, S extends Comparable<? super S>> List<T> getLower(T en) {
List<T> list = new ArrayList<>();
for (T t : en.getDeclaringClass().getEnumConstants()) {
if (t.getId().compareTo(en.getId()) < 0) {
list.add(t);
}
}
return list;
}
}
ถ้าฉันสร้างIdentifiable
enum
:
public enum MyEnum implements Identifiable<Integer> {
FIRST(1), SECOND(2);
private int id;
private MyEnum(int id) {
this.id = id;
}
public Integer getId() {
return id;
}
}
จากนั้นฉันจะได้รับโดยid
วิธีนี้:
MyEnum e = IdUtil.get(MyEnum.class, 1);
เนื่องจาก Enums สามารถใช้อินเตอร์เฟสได้จึงสามารถใช้สำหรับการบังคับใช้รูปแบบซิงเกิลที่เข้มงวด การพยายามทำให้คลาสมาตรฐานเป็นแบบซิงเกิลอนุญาตให้ ...
Enums เป็น singletons ช่วยป้องกันปัญหาด้านความปลอดภัยเหล่านี้ นี่อาจเป็นหนึ่งในสาเหตุที่ทำให้ Enums ทำหน้าที่เป็นคลาสและใช้อินเทอร์เฟซ เพียงแค่เดา
ดู/programming/427902/java-enum-singletonและSingleton class ใน javaเพื่อการสนทนาเพิ่มเติม
for inheriting from your singleton and overriding your singleton's methods with something else
. คุณสามารถใช้ a final class
เพื่อป้องกันสิ่งนั้นได้
มันจำเป็นสำหรับการเพิ่ม - ถ้ามีคนใช้ API ที่คุณพัฒนาขึ้นจำนวนเงินที่คุณกำหนดจะคงที่ ไม่สามารถเพิ่มหรือแก้ไขได้ อย่างไรก็ตามถ้าคุณปล่อยให้มันใช้อินเตอร์เฟสคนที่ใช้ API สามารถพัฒนา enum ของตัวเองโดยใช้อินเตอร์เฟสเดียวกัน จากนั้นคุณสามารถลงทะเบียน enum นี้ด้วยตัวจัดการ enum ซึ่งจะทำการรวม enums เข้ากับอินเตอร์เฟสมาตรฐาน
แก้ไข: @Helper Method มีตัวอย่างที่สมบูรณ์แบบ ลองนึกถึงการมีห้องสมุดอื่น ๆ ที่กำหนดผู้ประกอบการรายใหม่แล้วบอกกับผู้จัดการของชั้นว่า มิฉะนั้นคุณจะสามารถกำหนดผู้ประกอบการในรหัสของคุณเท่านั้น - ไม่มีความสามารถในการขยาย
Enums เป็นเพียงคลาสปลอมตัวดังนั้นส่วนใหญ่สิ่งที่คุณสามารถทำได้กับคลาสที่คุณสามารถทำได้ด้วย Enum
ฉันไม่สามารถนึกถึงเหตุผลที่ enum ไม่สามารถใช้ส่วนติดต่อได้ในเวลาเดียวกันฉันไม่สามารถคิดเหตุผลที่ดีสำหรับพวกเขาได้เช่นกัน
ฉันจะบอกว่าเมื่อคุณเริ่มเพิ่มสิ่งต่าง ๆ เช่นอินเทอร์เฟซหรือวิธีการ enum คุณควรพิจารณาทำให้คลาสแทน แน่นอนฉันแน่ใจว่ามีกรณีที่ถูกต้องสำหรับการทำสิ่งที่ไม่ใช่แบบดั้งเดิม enum และเนื่องจากขีด จำกัด จะเป็นสิ่งประดิษฐ์ฉันจึงยินยอมให้ผู้คนทำในสิ่งที่พวกเขาต้องการ
ตัวอย่างเช่นถ้าคุณมี Logger enum จากนั้นคุณควรมีวิธีการบันทึกเช่นดีบักข้อมูลคำเตือนและข้อผิดพลาดในอินเทอร์เฟซ มันทำให้โค้ดของคุณมีการรวมกันอย่างหลวม ๆ
การใช้งานทั่วไปส่วนใหญ่สำหรับสิ่งนี้คือการรวมค่าของสอง enums ไว้ในกลุ่มเดียวและปฏิบัติต่อพวกมันในทำนองเดียวกัน ยกตัวอย่างเช่นดูวิธีการเข้าร่วมผักและผล Vegatables
หนึ่งในกรณีที่ดีที่สุดสำหรับฉันที่จะใช้ Enum กับอินเทอร์เฟซคือตัวกรองเพรดิเคต เป็นวิธีที่ยอดเยี่ยมมากในการแก้ไขการขาดอาปาเช่คอลเลคชั่น (ถ้าไม่มีไลบรารีอื่น ๆ )
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
public class Test {
public final static String DEFAULT_COMPONENT = "Default";
enum FilterTest implements Predicate {
Active(false) {
@Override
boolean eval(Test test) {
return test.active;
}
},
DefaultComponent(true) {
@Override
boolean eval(Test test) {
return DEFAULT_COMPONENT.equals(test.component);
}
}
;
private boolean defaultValue;
private FilterTest(boolean defautValue) {
this.defaultValue = defautValue;
}
abstract boolean eval(Test test);
public boolean evaluate(Object o) {
if (o instanceof Test) {
return eval((Test)o);
}
return defaultValue;
}
}
private boolean active = true;
private String component = DEFAULT_COMPONENT;
public static void main(String[] args) {
Collection<Test> tests = new ArrayList<Test>();
tests.add(new Test());
CollectionUtils.filter(tests, FilterTest.Active);
}
}
โพสต์ข้างต้นที่กล่าวถึงกลยุทธ์นั้นไม่ได้เครียดเพียงพอที่การใช้รูปแบบกลยุทธ์ที่มีน้ำหนักเบาโดยใช้ enums จะทำให้คุณได้รับ:
public enum Strategy {
A {
@Override
void execute() {
System.out.print("Executing strategy A");
}
},
B {
@Override
void execute() {
System.out.print("Executing strategy B");
}
};
abstract void execute();
}
คุณสามารถมีกลยุทธ์ทั้งหมดของคุณในที่เดียวโดยไม่จำเป็นต้องมีหน่วยการรวบรวมแยกต่างหากสำหรับแต่ละหน่วย คุณจะได้รับการจัดส่งแบบไดนามิกที่ดีเพียงแค่:
Strategy.valueOf("A").execute();
ทำให้ java อ่านเกือบจะเป็นภาษาที่พิมพ์ดีอย่างหลวม ๆ !
Enums เป็นเหมือนเรียน Java พวกเขาสามารถมีก่อสร้าง, วิธีอื่น ๆ new EnumName()
มีเพียงสิ่งเดียวที่คุณไม่สามารถทำอะไรกับพวกเขาก็คือ อินสแตนซ์ถูกกำหนดไว้ล่วงหน้าในการประกาศ enum ของคุณ
enum Foo extends SomeOtherClass
อะไร ดังนั้นจึงไม่ใช่สิ่งเดียวกันกับชั้นเรียนปกติที่จริงแล้วค่อนข้างแตกต่างกัน
ตำแหน่งอื่น:
public enum ConditionsToBeSatisfied implements Predicate<Number> {
IS_NOT_NULL(Objects::nonNull, "Item is null"),
IS_NOT_AN_INTEGER(item -> item instanceof Integer, "Item is not an integer"),
IS_POSITIVE(item -> item instanceof Integer && (Integer) item > 0, "Item is negative");
private final Predicate<Number> predicate;
private final String notSatisfiedLogMessage;
ConditionsToBeSatisfied(final Predicate<Number> predicate, final String notSatisfiedLogMessage) {
this.predicate = predicate;
this.notSatisfiedLogMessage = notSatisfiedLogMessage;
}
@Override
public boolean test(final Number item) {
final boolean isNotValid = predicate.negate().test(item);
if (isNotValid) {
log.warn("Invalid {}. Cause: {}", item, notSatisfiedLogMessage);
}
return predicate.test(item);
}
}
และการใช้:
Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);
เมื่อสร้างค่าคงที่ในไฟล์ jar มักจะเป็นประโยชน์ในการให้ผู้ใช้ขยายค่า enum เราใช้ enums สำหรับคีย์ PropertyFile และค้างอยู่เพราะไม่มีใครสามารถเพิ่มคีย์ใหม่ได้! ด้านล่างจะทำงานได้ดีขึ้นมาก
ได้รับ:
public interface Color {
String fetchName();
}
และ:
public class MarkTest {
public static void main(String[] args) {
MarkTest.showColor(Colors.BLUE);
MarkTest.showColor(MyColors.BROWN);
}
private static void showColor(Color c) {
System.out.println(c.fetchName());
}
}
หนึ่งอาจมีหนึ่ง enum ในขวด:
public enum Colors implements Color {
BLUE, RED, GREEN;
@Override
public String fetchName() {
return this.name();
}
}
และผู้ใช้สามารถขยายเพื่อเพิ่มสีของเขาเอง:
public enum MyColors implements Color {
BROWN, GREEN, YELLOW;
@Override
public String fetchName() {
return this.name();
}
}
นี่คือเหตุผลของฉันทำไม ...
ฉันได้เติม JavaFX ComboBox ด้วยค่าของ Enum ฉันมีอินเทอร์เฟซที่สามารถระบุตัวตนได้ (ระบุวิธีการหนึ่ง: ระบุ) ที่ช่วยให้ฉันระบุว่าวัตถุใดที่ระบุตัวเองในแอปพลิเคชันของฉันเพื่อวัตถุประสงค์ในการค้นหา อินเทอร์เฟซนี้ช่วยให้ฉันสามารถสแกนรายการของวัตถุชนิดใดก็ได้ (เขตข้อมูลใดก็ตามที่วัตถุอาจใช้เพื่อระบุตัวตน) เพื่อจับคู่ข้อมูลประจำตัว
ฉันต้องการค้นหาการจับคู่สำหรับค่าตัวตนในรายการ ComboBox ของฉัน เพื่อที่จะใช้ความสามารถนี้ใน ComboBox ของฉันที่มีค่า Enum ฉันจะต้องสามารถใช้งานอินเทอร์เฟซที่สามารถระบุตัวตนได้ใน Enum ของฉัน (ซึ่งในขณะที่มันเกิดขึ้น
ฉันใช้ enum ด้านในในอินเทอร์เฟซที่อธิบายกลยุทธ์เพื่อควบคุมอินสแตนซ์ (แต่ละกลยุทธ์คือซิงเกิลตัน) จากที่นั่น
public interface VectorizeStrategy {
/**
* Keep instance control from here.
*
* Concrete classes constructors should be package private.
*/
enum ConcreteStrategy implements VectorizeStrategy {
DEFAULT (new VectorizeImpl());
private final VectorizeStrategy INSTANCE;
ConcreteStrategy(VectorizeStrategy concreteStrategy) {
INSTANCE = concreteStrategy;
}
@Override
public VectorImageGridIntersections processImage(MarvinImage img) {
return INSTANCE.processImage(img);
}
}
/**
* Should perform edge Detection in order to have lines, that can be vectorized.
*
* @param img An Image suitable for edge detection.
*
* @return the VectorImageGridIntersections representing img's vectors
* intersections with the grids.
*/
VectorImageGridIntersections processImage(MarvinImage img);
}
ความจริงที่ว่า enum ใช้กลยุทธ์นั้นสะดวกเพื่อให้คลาส enum ทำหน้าที่เป็นพร็อกซีสำหรับอินสแตนซ์ที่ล้อมรอบ ซึ่งใช้อินเทอร์เฟซ
มันเป็นส่วนหนึ่งของ strategyEnumProxy: P รหัส clent มีลักษณะดังนี้:
VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);
ถ้ามันไม่ได้ใช้อินเทอร์เฟซมันเคยเป็น:
VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);