Cara Mengganti Banyak jika Penyataan di Java

1. Gambaran keseluruhan

Konstruk keputusan adalah bahagian penting dari mana-mana bahasa pengaturcaraan. Tetapi kita membuat pengekodan sebilangan besar jika terdapat penyataan yang menjadikan kod kita lebih rumit dan sukar dijaga.

Dalam tutorial ini, kita akan melalui pelbagai cara untuk menggantikan penyataan bersarang jika .

Mari kita meneroka pelbagai pilihan bagaimana kita dapat mempermudah kodnya.

2. Kajian Kes

Selalunya kita menghadapi logik perniagaan yang melibatkan banyak syarat, dan masing-masing memerlukan pemprosesan yang berbeza. Demi demo, mari kita ambil contoh kelas Kalkulator . Kami akan mempunyai kaedah yang mengambil dua nombor dan operator sebagai input dan mengembalikan hasil berdasarkan operasi:

public int calculate(int a, int b, String operator) { int result = Integer.MIN_VALUE; if ("add".equals(operator)) { result = a + b; } else if ("multiply".equals(operator)) { result = a * b; } else if ("divide".equals(operator)) { result = a / b; } else if ("subtract".equals(operator)) { result = a - b; } return result; }

Kami juga dapat melaksanakannya menggunakan pernyataan suis :

public int calculateUsingSwitch(int a, int b, String operator) { switch (operator) { case "add": result = a + b; break; // other cases } return result; }

Dalam perkembangan biasa, pernyataan jika boleh tumbuh jauh lebih besar dan lebih kompleks . Juga, penyata suis tidak sesuai dengan baik apabila terdapat syarat-syarat yang kompleks .

Kesan sampingan lain dari membina keputusan yang bersarang adalah ia tidak dapat dikendalikan. Sebagai contoh, jika kita perlu menambah operator baru, kita harus menambahkan pernyataan if baru dan melaksanakan operasi.

3. Pemfaktoran semula

Mari terokai pilihan alternatif untuk menggantikan kompleks jika pernyataan di atas menjadi kod yang lebih mudah dan terkawal.

3.1. Kelas Kilang

Sering kali kita menemui keputusan yang akhirnya melakukan operasi serupa di setiap cawangan. Ini memberi peluang untuk mengekstrak kaedah kilang yang mengembalikan objek dari jenis tertentu dan melakukan operasi berdasarkan tingkah laku objek konkrit .

Sebagai contoh kita, mari kita mentakrifkan Operasi antara muka yang mempunyai tunggal memohon kaedah:

public interface Operation { int apply(int a, int b); }

Kaedah ini mengambil dua nombor sebagai input dan mengembalikan hasilnya. Mari tentukan kelas untuk melakukan penambahan:

public class Addition implements Operation { @Override public int apply(int a, int b) { return a + b; } }

Kami sekarang akan melaksanakan kelas kilang yang mengembalikan keadaan Operasi berdasarkan operator yang diberikan:

public class OperatorFactory { static Map operationMap = new HashMap(); static { operationMap.put("add", new Addition()); operationMap.put("divide", new Division()); // more operators } public static Optional getOperation(String operator) { return Optional.ofNullable(operationMap.get(operator)); } }

Sekarang, di kelas Kalkulator , kita boleh meminta kilang untuk mendapatkan operasi yang relevan dan menggunakan nombor sumber:

public int calculateUsingFactory(int a, int b, String operator) { Operation targetOperation = OperatorFactory .getOperation(operator) .orElseThrow(() -> new IllegalArgumentException("Invalid Operator")); return targetOperation.apply(a, b); }

Dalam contoh ini, kita telah melihat bagaimana tanggungjawab itu diberikan kepada benda-benda yang digabungkan secara longgar yang dilayan oleh kelas kilang. Tetapi mungkin ada kemungkinan di mana bersarang jika pernyataan hanya beralih ke kelas kilang yang mengalahkan tujuan kita.

Sebagai alternatif, kita dapat mempertahankan repositori objek dalam Peta yang dapat ditanyakan untuk pencarian cepat . Seperti yang telah kita lihat OperatorFactory # operationMap memenuhi tujuan kita. Kami juga dapat menginisialisasi Peta pada waktu runtime dan mengkonfigurasinya untuk mencari.

3.2. Penggunaan Bilangan

Selain penggunaan Peta, kami juga dapat menggunakan Enum untuk melabelkan logik perniagaan tertentu . Selepas itu, kita boleh menggunakannya sama ada dalam penyataan bersarang jika pernyataan atau menukar pernyataan kes . Sebagai alternatif, kita juga dapat menggunakannya sebagai kilang objek dan menyusun strategi untuk melaksanakan logik perniagaan yang berkaitan.

Itu akan mengurangkan bilangan bersarang jika pernyataan juga dan menyerahkan tanggungjawab kepada nilai Enum individu .

Mari lihat bagaimana kita dapat mencapainya. Pada mulanya, kita perlu menentukan Enum kita :

public enum Operator { ADD, MULTIPLY, SUBTRACT, DIVIDE }

Seperti yang dapat kita perhatikan, nilainya adalah label dari operator yang berbeza yang akan digunakan lebih jauh untuk pengiraan. Kami selalu mempunyai pilihan untuk menggunakan nilai sebagai keadaan yang berlainan dalam bersarang jika pernyataan atau menukar kes, tetapi mari kita merancang cara alternatif untuk mendelegasikan logik ke Enum itu sendiri.

Kami akan menentukan kaedah untuk setiap nilai Enum dan melakukan pengiraan. Contohnya:

ADD { @Override public int apply(int a, int b) { return a + b; } }, // other operators public abstract int apply(int a, int b);

Dan kemudian di kelas Kalkulator , kita dapat menentukan kaedah untuk melakukan operasi:

public int calculate(int a, int b, Operator operator) { return operator.apply(a, b); }

Sekarang, kita boleh meminta kaedah yang dengannya menukarkan String nilai kepada Operator dengan menggunakan Operator # valueOf () kaedah :

@Test public void whenCalculateUsingEnumOperator_thenReturnCorrectResult() { Calculator calculator = new Calculator(); int result = calculator.calculate(3, 4, Operator.valueOf("ADD")); assertEquals(7, result); }

3.3. Corak Perintah

Dalam perbincangan sebelumnya, kita telah melihat penggunaan kelas kilang untuk mengembalikan contoh objek perniagaan yang betul untuk operator yang diberikan. Kemudian, objek perniagaan digunakan untuk melakukan pengiraan di Kalkulator .

Kita juga dapat merancang kaedah kalkulator # kalkulator untuk menerima perintah yang dapat dijalankan pada input . Ini akan menjadi kaedah lain untuk menggantikan penyataan bersarang jika .

Kami akan menentukan antara muka Perintah kami terlebih dahulu :

public interface Command { Integer execute(); }

Seterusnya, mari kita laksanakan AddCommand:

public class AddCommand implements Command { // Instance variables public AddCommand(int a, int b) { this.a = a; this.b = b; } @Override public Integer execute() { return a + b; } }

Akhirnya, mari kita memperkenalkan kaedah baru dalam Kalkulator yang menerima dan melaksanakan Perintah :

public int calculate(Command command) { return command.execute(); }

Next, we can invoke the calculation by instantiating an AddCommand and send it to the Calculator#calculate method:

@Test public void whenCalculateUsingCommand_thenReturnCorrectResult() { Calculator calculator = new Calculator(); int result = calculator.calculate(new AddCommand(3, 7)); assertEquals(10, result); }

3.4. Rule Engine

When we end up writing a large number of nested if statements, each of the conditions depicts a business rule which has to be evaluated for the correct logic to be processed. A rule engine takes such complexity out of the main code. A RuleEngine evaluates the Rules and returns the result based on the input.

Let's walk through an example by designing a simple RuleEngine which processes an Expression through a set of Rules and returns the result from the selected Rule. First, we'll define a Rule interface:

public interface Rule { boolean evaluate(Expression expression); Result getResult(); }

Second, let's implement a RuleEngine:

public class RuleEngine { private static List rules = new ArrayList(); static { rules.add(new AddRule()); } public Result process(Expression expression) { Rule rule = rules .stream() .filter(r -> r.evaluate(expression)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule")); return rule.getResult(); } }

The RuleEngine accepts an Expression object and returns the Result. Now, let's design the Expression class as a group of two Integer objects with the Operator which will be applied:

public class Expression { private Integer x; private Integer y; private Operator operator; }

Dan akhirnya mari kita tentukan kelas AddRule tersuai yang menilai hanya apabila Operasi ADD ditentukan:

public class AddRule implements Rule { @Override public boolean evaluate(Expression expression) { boolean evalResult = false; if (expression.getOperator() == Operator.ADD) { this.result = expression.getX() + expression.getY(); evalResult = true; } return evalResult; } }

Kami sekarang akan memanggil RuleEngine dengan Ungkapan :

@Test public void whenNumbersGivenToRuleEngine_thenReturnCorrectResult() { Expression expression = new Expression(5, 5, Operator.ADD); RuleEngine engine = new RuleEngine(); Result result = engine.process(expression); assertNotNull(result); assertEquals(10, result.getValue()); }

4. Kesimpulan

Dalam tutorial ini, kami meneroka beberapa pilihan yang berbeza untuk mempermudah kod kompleks. Kami juga belajar bagaimana untuk menggantikan penyataan jika penyataan dengan menggunakan corak reka bentuk yang berkesan.

Seperti biasa, kita dapat mencari kod sumber lengkap melalui repositori GitHub.