接口和抽象类的区别其实挺多的。比如以下这些:
接口和抽象类,最明显的区别就是接口只定义了一些方法而已,再不考虑Java 8 中的default方法情况下,接口中只有抽象方法,是没有实现的代码的。(Java 8 中可以有默认方法)
抽象类中的抽象方法可以有public 、 protected 、 和default 这些修饰符,而接口中默认修饰符是public。不可以使用其他修饰符。
抽象类可以有构造器,接口不能有构造器。
接口可以被实现,抽象类可以被继承。
一个类可以实现多个接口,但只能继承一个抽象类。接口支持多重继承,即一个接口可以继承多个其它接口。
public interface HollisTestService extends InitializingBean,DisposableBean {}
接口和抽象类的职责不一样。接口主要用于制定规范,因为我们提倡也经常使用的都是面向接口棉城。而抽象类主要目的是为了复用,比较典型的就是模板方法模式。
了解完这些,我们使用Java代码来规整一下:
// 定义一个接口,名为Animal
interface Animal {
// 定义一个抽象方法,用于发出动物的叫声
void makeSound();
}
// 定义一个接口,名为Mammal
interface Mammal extends Animal {
// 定义一个抽象方法,用于哺乳动物生育
public abstract void giveBirth();
}
// 定义一个抽象类,名为Reptile
abstract class Reptile implements Animal {
// 定义一个抽象方法,用于爬行动物移动
public abstract void move();
}
// 定义一个实现了Mammal接口的类,名为Dog
class Dog implements Mammal {
// 重写makeSound方法,实现狗的叫声
@Override
public void makeSound() {
System.out.println("汪汪!");
}
// 重写giveBirth方法,实现狗的生育行为(这里只是模拟,实际狗的生育行为更复杂)
@Override
public void giveBirth() {
System.out.println("汪汪!");
}
}
// 定义一个继承了Reptile抽象类的类,名为Snake
class Snake extends Reptile {
// 重写move方法,实现蛇的移动方式(这里只是模拟,实际蛇的移动方式更复杂)
@Override
public void move() {
System.out.println("蜿蜒爬行...");
}
}
// 定义一个实现了Animal接口的类,名为Cat
class Cat implements Animal {
// 重写makeSound方法,实现猫的叫声
@Override
public void makeSound() {
System.out.println("喵喵!");
}
}
// 主函数,测试代码
public class Main {
public static void main(String[] args) {
// 创建Dog对象并调用makeSound和giveBirth方法
Dog dog = new Dog();
dog.makeSound(); // 输出 "汪汪!"
dog.giveBirth(); // 输出 "汪汪!"
// 创建Snake对象并调用move方法
Snake snake = new Snake();
snake.move(); // 输出 "蜿蜒爬行..."
// 创建Cat对象并调用makeSound方法(注意Cat没有实现giveBirth方法)
Cat cat = new Cat();
cat.makeSound(); // 输出 "喵喵!"
}
}
以上演示了接口和抽象类的复杂使用。Animal接口定义了一个makeSound方法,而Mammal接口继承了Animal接口并定义了一个giveBirth方法。Reptile抽象类实现了Animal接口并定义了一个move方法。Dog类实现了Mammal接口,而Snake类继承了Reptile抽象类。在主函数中,我们创建了Dog、Snake和Cat对象,并调用了它们的方法。这个例子展示了多个接口和抽象类的组合使用。
所以,当我们想要定义标准、规范的时间,就是用接口。当我们想要复用代码的时候,就使用抽象类。
一般实际开发中,我们会先把接口暴露给外部,然后业务代码中实现接口。如果多个实现类中有相同可复用的代码,则在接口和实现类中加一层抽象类,将公用部分代码抽出到抽象类中。可以参考一下模板方法模式,这是一个很好理解接口、抽象类和实现类之间关系的设计模式。
模板方法模式是一种行为设计模式,他的主要作用就是复用代码。在很多时候,我们的代码中可能会有一些公共的部分并且还有一些定制的部分,那么公共这部分就可以定义在一个父类中,然后将定制的部分实现在子类中。这样了类可以根据需要扩展或重写父类的方法,而不需要改变算法的结构。
我们通常会把模板方法模式和策略模式一起使用,因为当我们使用策略模式的时候,会把具体的策略实现在策略服务里面,但是还剩下一些通用的逻辑,就可以通过模板方法模式进行复用。
我们拿一个常见的优惠券作为示例,假设我们需要定义一个优惠券的申请服务。
abstract class Coupon {
// 模板方法,定义优惠券的应用流程
public final oid applyCoupon() {
if (isCouponValid()) {
if (isEligibleForDiscount()) {
applyDiscount();
}
displayConfirmation();
} else {
displayInvalidCouponMessage();
}
}
// 具体方法,用于判断优惠券是否有效
protected boolean isCouponValid() {
// 具体的判断逻辑,子类可以重写该方法来实现特定的有效性判断
return true;
}
//具体方法,用于判断用户是否符合优惠券的折扣条件
protected boolean isEligibleForDiscount() {
//具体的判断逻辑,子类可以重写该方法来实现特定的条件判断
return true;
}
//具体方法,用于判断用户是否符合优惠券的折扣条件
protected boolean isEligibleForDiscount() {
//具体的判断逻辑,子类可以重写该方法来实现特定的条件判断
return true;
}
//抽象方法,由子类实现具体的优惠券折扣逻辑
protected abstract void applyDiscount();
// 抽象方法,由子类实现具体的优惠券确认展示逻辑
protected abstract void displayConfirmation();
// 具体方法,用于展示无效优惠券的信息
protected void displayInvalidCouponMessage() {
System.out.printIn("无效优惠券!“);
}
}
以上是一个抽象类。这个类中有一个具体的方法applyCoupon,其中定义了一个优惠券申请的具体实现,并且编排了多个其他的方法。
这就是一个典型的模板方法。我们可以基于这个抽象类来定义具体的实现:
class PercentageCoupon extends Coupon {
@Override
protected void applyDiscount() {
// 具体的百分比折扣逻辑
System.out.printIn("应用百分比折扣优惠!);
}
@Override
protected void displayConfirmation() {
// 具体的百分比优惠券确认展示逻辑
System.out.printIn("百分比折扣优惠确认!");
}
}
class FixedAmountCoupon extends Coupon {
@Override
protected void applyDiscount() {
// 具体的固定金额折扣逻辑
System.out.println("应用固定金额优惠!“);
}
@Override
protected void displayConfirmation() {
// 具体的固定金额优惠券确认展示逻辑
System.out.printIn("固定金额优惠确认!");
}
}
以上就是两个具体的实现,分别继承Coupon抽象类,并且实现其中的部分方法就可以了。
这样我们在实际使用时,可以直接使用FixedAmountCoupon 和 PercentageCoupon 类,并且直接调用它的applyCoupon方法就行了,如:
public class Main {
public static void main(String[] args) {
Coupon percentageCoupon = new PercentageCoupon();
percentageCoupon.applyCoupon();
System.out.println("-------------------------");
Coupon fixedAmountCoupon = new FixedAmountCoupon();
fixedAmountCoupon.applyCoupon();
}
}
看到这里,模板方法已经告一段落了,我们思考一下,如果在面试过程中,我们奇葩面试官问:你在**工作中是如何使用设计模式的?**那我们思考一下,脑子有思路吗?
没有思路也没关系!我们见招拆招,我给大家聊一下!
工作中常用的设计模式有很多,如单例、工厂、策略、模板等。一般在工作中,是可以把策略、工厂和模板一起结合着来使用的。
当我们需要有多个具体的策略服务的时候,那不同的内容放到策略服务中,那些公共的东西就可以抽象出来放到模板方法中了。那这些策略服务该如何管理呢?什么时候用什么策略服务呢?这时候就可以借助工广来管理这些服务。
如以下例子,我们需要定义一个支付服务,里面有一个支付方法:
public interface Payservice {
public void pay(PayRequest payRequest);
}
class PayRequest {
}
这是一个单独的接口,只定义了一个方法,那么我们再把所有支付渠道中公共的代码抽取出来,定义一个抽象类:
public abstract class AbstractPayService implements PayService {
@Override
public void pay(PayRequest payRequest) {
//前置检查
validateRequest(payRequest);
//支付核心逻辑
doPay(payRequest);
//后置处理
postPay(payRequest);
}
public abstract void doPay(PayRequest payRequest);
private void postPay(PayRequest payRequest) {
//支付成功的后置处理
}
public void validateRequest(PayRequest payRequest) {
//参数检查
}
}
这个抽象类中首先把pav方法给实现了,然后编排了几个其他的方法,这些公共的方法在抽象类中直接实现了,具体的支付核心实现,留给实现类去实现就行了。
然后我们就可以定义多个策略服务了:
@Service
public class AlipayPayService extends AbstractPayService {
@Override
public void doPay(PayRequest payRequest) {
//支付宝支付逻辑
}
}
@Service
public class WechatPayService extends AbstractPayService {
@Override
public void doPay(PayRequest payRequest) {
//微信支付逻辑
}
}
这些服务协议定好了以后,需要一个地方统一管理,那就定义一个工厂吧:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class PayServiceFactory {
@Autowired
public Map<String,ayService> payServiceMap = new ConcurrentHashMap<>();
public PaySerice getPayService(String payChannel) {
// alipay -> alipayPayService
// wechat -> wechatPayService
return payServiceMap.get(payChannel +"PayService");
}
}
在工厂中,把PayService的所有实现全部都注入到payServiceMap中,然后再需要用的是,直接调他的getPayService方法就行了
这样,在使用的时候,只需要通过工厂就能获取对应的策略服务进行服务调用了:
public class PayDomainService {
@Autowired
PayServiceFactory payServiceFactory;
public void pay(PayRequest payRequest) {
String payChannel = payRequest.getPayChannel();
payServiceFactory.getPayService(payChannel).pay(payRequest);
}
}
以上,我们借助了Spring,结合了策略、模板以及工厂,实现了我们想要的功能,通过多种设计模式,减少重复代码,提升可维护性,也让代码更容易阅读和理解。
接博主上一篇博文: 如何理解面向对象和面向过程