Application skeleton implementation in Java

Duplicate code in the program? Skeletal Implementation (Skeletal Implementation) cooperates with abstract classes through interfaces, allowing you to get rid of duplication and leave useful code in the program.


Skeleton implementation is a design, we can enjoy the benefits of interfaces and abstract classes at the same time.


Java Collection API has adopted this design: AbstractSet, AbstractMap, etc. are all skeleton implementation cases. Skeleton interfaces are also mentioned in Joshua Bloch's "Effective Java" book.


In this article, we will discuss how to efficiently design a system so that it can simultaneously utilize the characteristics of interfaces and abstract classes.


Let us try to understand through a practical question.


Suppose we want to create different types of vending machines. To purchase a product from a vending machine, you need to activate the vending machine, select the product, pay, and then pick up the goods.


After the pick-up is complete, the vending machine should stop operating.


1. Method One


We can create a vending machine interface for different product types. In order for the interface to work, we also need to provide a concrete implementation for the vending machine.


1.1 Code


Ivending.java

```java
package com.example.skeletal;
public interface Ivending {
   void start();
   void chooseProduct();
   void stop();
   void process();
}
```


CandyVending.java

```java
package com.example.skeletal;
public class CandyVending implements Ivending {
   @Override
   public void start()
{
       System.out.println("Start Vending machine");
   }

   @Override
   public void chooseProduct()
{
       System.out.println("Produce different candies");
       System.out.println("Choose a type of candy");
       System.out.println("Pay for candy");
       System.out.println("Collect candy");
   }
   
   @Override
   public void stop()
{
       System.out.println("Stop Vending machine");
   }

   @Override
   public void process()
{
       start();
       chooseProduct();
       stop();
   }
}
```


DrinkVending.java

```java
package com.example.skeletal;
public class DrinkVending implements Ivending {
   @Override
   public void start()
{
       System.out.println("Start Vending machine");
   }

   @Override
   public void chooseProduct()
{
       System.out.println("Produce diiferent soft drinks");
       System.out.println("Choose a type of soft drinks");
       System.out.println("pay for drinks");
       System.out.println("collect drinks");
   }

   @Override
   public void stop()
{
       System.out.println("stop Vending machine");
   }

   @Override
   public void process()
{
       start();
       chooseProduct();
       stop();
   }
}
```


VendingManager.java

```java
package com.example.skeletal;
public class VendingManager {
   public static void main(String[] args) {
       Ivending candy = new CandyVending();
       Ivending drink = new DrinkVending();
       candy.process();
       drink.process();
   }
}
```


Output result:


```shell
Start Vending machine
Produce different candies
Choose a type of candy
Pay for candy
Collect candy
Stop Vending machine
*********************
Start Vending machine
Produce diiferent soft drinks
Choose a type of soft drinks
Pay for drinks
Collect drinks
Stop Vending machine
```


For the sake of simplicity, I did not define a separate method for each step, but combined these steps in `chooseProduct()`.


Although it looks good, the code above "has some problems." If we check it carefully, we will find a lot of duplicate code. The `start()`, `stop()` and `process()` methods do the same thing in each implementation class.


When a specific implementation is added, the system code will be copied three times.


At this time, we can create a new tool class and put the common code in the tool class. However, doing so will undermine the "single responsibility principle", resulting in Shotgun surgery problem code.


Annotation: [Shotgun surgery][1] is an anti-pattern in software development, which occurs when developers add features to the application code base, which will span multiple implementations in one change.


[1]:https://en.wikipedia.org/wiki/Shotgun_surgery


1.2 Disadvantages of the interface


Since an interface is a convention and does not contain a method body, each implementation must implement all methods in the interface in accordance with the convention. Some methods may be repeated in specific implementations.


2. Method Two


通过抽象类弥补接口的不足。


2.1 代码


AbstractVending.java

```java
package com.example.skeletal;
public abstract class AbstractVending {
   public void start()
   
{
       System.out.println("Start Vending machine");
   }

   public abstract void chooseProduct();
   
   public void stop()
   
{
       System.out.println("Stop Vending machine");
   }
   
   public void process()
   
{
       start();
       chooseProduct();
       stop();
   }
}
```


CandyVending.java

```java
package com.example.skeletal;
public class CandyVending extends AbstractVending {
   @Override
   public void chooseProduct()
{
       System.out.println("Produce diiferent candies");
       System.out.println("Choose a type of candy");
       System.out.println("Pay for candy");
       System.out.println("Collect candy");
   }
}
```


DrinkVending.java

```java
package com.example.skeletal;
public class DrinkVending extends AbstractVending {
   @Override
   public void chooseProduct()
{
       System.out.println("Produce diiferent soft drinks");
       System.out.println("Choose a type of soft drinks");
       System.out.println("Pay for drinks");
       System.out.println("Collect drinks");
   }
}
```


VendingManager.java

```java
package com.example.skeletal;
public class VendingManager {
   public static void main(String[] args) {
       AbstractVending candy =  new CandyVending();
       AbstractVending drink =  new DrinkVending();
       candy.process();
       System.out.println("*********************");
       drink.process();
   }
}
```


这里我为抽象类提供了通用的代码,`CandyVending` 和 `DrinkVending` 都继承了 `AbstractVending`。这么做虽然消除了重复代码,但引入了一个新问题。


`CandyVending` 和 `DrinkVending` 继承了 `AbstractVending`,由于 Java 不支持多重集成因此不能继承其他类。


假如要添加一个 `VendingServicing` 类,负责清洁和检查自动售货机。在这种情况下,由于已经继承了 `AbstractVending`,因此不能继承 `VendingServicing`。这里可以新建组合(composition),但是必须把 `VendingMachine` 传入该组合,这会让 `VendingServicing` 和 `VendingMachine` 产生强耦合。


2.2 抽象类的缺点


由于菱形继承问题,Java 不支持多重继承。假如我们能够同时利用接口和抽象类的优点就太好了。


还是有办法的。


译注:菱形继承问题。两个子类继承同一个父类,而又有子类又分别继承这两个子类,产生二义性问题。


3. 抽象接口或骨架实现


要完成骨架实现:


  1. 创建接口。

  2. 创建抽象类来实现该接口,并实现公共方法。

  3. 在子类中创建一个私有内部类,继承抽象类。现在把外部调用委托给抽象类,该类可以在使用通用方法同时继承和实现任何接口。


3.1 代码


Ivending.java

```java
package com.example.skeletal;
public interface Ivending {
   void start();
   void chooseProduct();
   void stop();
   void process();
}
```


VendingService.java

```java
package com.example.skeletal;
public class VendingService {
   public void service()
   
{
       System.out.println("Clean the vending machine");
   }
}
```


AbstractVending.java

```java
package com.example.skeletal;
public abstract class AbstractVending implements Ivending {
   public void start()
   
{
       System.out.println("Start Vending machine");
   }
   public void stop()
   
{
       System.out.println("Stop Vending machine");
   }
   public void process()
   
{
       start();
       chooseProduct();
       stop();
   }
}
```


CandyVending.java

```java
package com.example.skeletal;
public class CandyVending  implements Ivending {
   private class AbstractVendingDelegator extends AbstractVending
   {
       @Override
       public void chooseProduct()
{
           System.out.println("Produce diiferent candies");
           System.out.println("Choose a type of candy");
           System.out.println("Pay for candy");
           System.out.println("Collect candy");
       }
   }

   AbstractVendingDelegator delegator = new AbstractVendingDelegator();

   @Override
   public void start()
{
       delegator.start();
   }
   @Override
   public void chooseProduct()
{
       delegator.chooseProduct();
   }
   @Override
   public void stop()
{
       delegator.stop();
   }
   @Override
   public void process()
{
       delegator.process();
   }
}
```


DrinkVending.java

```java
package com.example.skeletal;
public class DrinkVending extends VendingService  implements Ivending {
   private class AbstractVendingDelegator extends AbstractVending
   {
       @Override
       public void chooseProduct()
{
           System.out.println("Produce diiferent soft drinks");
           System.out.println("Choose a type of soft drinks");
           System.out.println("pay for drinks");
           System.out.println("collect drinks");
       }
   }
   AbstractVendingDelegator delegator = new AbstractVendingDelegator();
   @Override
   public void start()
{
       delegator.start();
   }
   @Override
   public void chooseProduct()
{
       delegator.chooseProduct();
   }
   @Override
   public void stop()
{
       delegator.stop();
   }
   @Override
   public void process()
{
       delegator.process();
   }
}
```


VendingManager.java

```java
package com.example.skeletal;
public class VendingManager {
   public static void main(String[] args) {
       Ivending candy = new CandyVending();
       Ivending drink = new DrinkVending();
       candy.process();
       System.out.println("*********************");
       drink.process();
       if(drink instanceof VendingService)
       {
           VendingService vs = (VendingService)drink;
           vs.service();
       }
   }
}
```


```shell
Start Vending machine
Produce diiferent candies
Choose a type of candy
Pay for candy
Collect candy
Stop Vending machine
*********************
Start Vending machine
Produce diiferent soft drinks
Choose a type of soft drinks
Pay for drinks
Collect drinks
Stop Vending machine
Clean the vending machine
```


上面的设计中,首先创建了一个接口,然后创建了一个抽象类,在这个类中定义了所有通用的实现。然后,为每个子类实现一个 delegator 类。通过 delegator 将调用转给 `AbstractVending`。


3.2 骨架实现的好处


  1. 子类可继承其他类,比如 `DrinkVending`。

  2. 通过将调用委托给抽象类消除重复代码。

  3. 子类可根据需要实现其他的接口。


4. 总结


当接口有公用方法时可以创建抽象类,使用子类作为委派器,建议使用骨架实现。



Guess you like

Origin blog.51cto.com/15082395/2590380