【️接口和抽象类的区别,如何选择?】

news2024/11/24 17:33:12

在这里插入图片描述

✅接口和抽象类的区别,如何选择?

  • ✅ 接口和抽象类的区别
      • ✅方法定义
      • ✅修饰符
      • ✅构造器
      • ✅继承和实现
      • ✅单继承 、 多实现
      • ✅职责不同
  • ✅什么是模板方法模式,有哪些应用呢?
    • ✅典型理解
      • ✅示例
      • 💡思考
  • ✅你在工作中是如何使用设计模式的?

✅ 接口和抽象类的区别

接口和抽象类的区别其实挺多的。比如以下这些:

✅方法定义

接口和抽象类,最明显的区别就是接口只定义了一些方法而已,再不考虑Java 8 中的default方法情况下,接口中只有抽象方法,是没有实现的代码的。(Java 8 中可以有默认方法)

✅修饰符

抽象类中的抽象方法可以有publicprotected 、 和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,结合了策略、模板以及工厂,实现了我们想要的功能,通过多种设计模式,减少重复代码,提升可维护性,也让代码更容易阅读和理解。

接博主上一篇博文: 如何理解面向对象和面向过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1316740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Swin-Transformer 在图像识别中的应用

1. 卷积神经网络简单介绍 图像识别任务主要利用神经网络对图像进行特征提取&#xff0c;最后通过全连接层将特征和分类个数进行映射。传统的网络是利用线性网络对图像进行分类&#xff0c;然而图像信息是二维的&#xff0c;一般来说&#xff0c;图像像素点和周围邻域像素点相关…

【MISRA C 2012】Rule 5.4 宏标识符应该是不同的

1. 规则1.1 原文1.2 分类 2. 关键描述3. Example4. 代码实例 1. 规则 1.1 原文 1.2 分类 规则5.4&#xff1a;宏标识符应该是不同的 Required要求类规范。 2. 关键描述 该规则要求&#xff0c;当定义宏时&#xff0c;其名称与: •当前定义的其他宏的名称;和 •参数的名称。…

网线市场现状与发展趋势预测

随着物联网、5G、云计算等技术的迅速发展&#xff0c;全球对于高速、稳定的网络需求急剧增长&#xff0c;这进一步推动了网线市场的发展。各种网络应用场景&#xff0c;从家庭到企业、数据中心到智能城市&#xff0c;都需要大量的高质量网线来支持数据传输和通信需求。本文将对…

windows 10 安装和配置nginx

1 下载nginx 1.1 下载地址&#xff1a;http://nginx.org/en/download.html 1.2 使用解压到安装目录 1.3 更改配置 conf目录下nginx.conf 修改为未被占用的端口&#xff0c;地址改成你的地址 server {listen 9999;server_name localhost;#charset koi8-r;#access_lo…

超文本传送协议HTTP

目录 HTTP简介&#xff1a; URL的格式&#xff1a; HTTP协议的特点&#xff1a; HTTP/1.0协议&#xff1a; HTTP/1.1协议&#xff1a; HTTP/2: HTTP代理服务器&#xff1a; HTTP的报文结构&#xff1a; 请求报文的特点&#xff1a; 响应报文的特点&#xff1a; Cook…

eNSP小实验---(简单混合)

实验目的&#xff1a;实现vlan10 vlan20 172网段用户互访 1.拓扑图 2.配置 PC1 其它同理 SW4 <Huawei> <Huawei>u t m Info: Current terminal monitor is off. <Huawei>sys <Huawei>sys Enter system view, return user view with CtrlZ. [Hua…

idea第一次提交到git(码云)

1.先创建一个仓库 2.将idea和仓库地址绑定 2.将idea和仓库地址绑定

升华 RabbitMQ:解锁一致性哈希交换机的奥秘【RabbitMQ 十】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 升华 RabbitMQ&#xff1a;解锁一致性哈希交换机的奥秘【RabbitMQ 十】 前言第一&#xff1a;该插件需求为什么需要一种更智能的消息路由方式&#xff1f;一致性哈希的基本概念&#xff1a; 第二&…

Vue学习计划-Vue2--VueCLi(七)nextTick、、浏览器本地缓存、脚手架配置代理

1. nextTick 语法&#xff1a; this.$nextTick(回调函数)作用&#xff1a;在下一次DOM更新结束后执行其指定的回调什么时候用&#xff1a; 当改变数据后&#xff0c;要基于更新后的新DOM进行某些操作时&#xff0c;要在nextTick所指定的回调函数中执行 **举个栗子&#xff1a;…

nodejs+vue+微信小程序+python+PHP运动项目推荐系统-计算机毕业设计推荐

运动项目推荐系统的整体架构确定以后&#xff0c;再来看运动项目推荐系统的主要功能模块图。整体的功能模块包括前台和后台&#xff0c;前台只要实现了注册用户功能&#xff0c;主要的页面&#xff0c;包括首页&#xff0c;体育资讯&#xff0c;体育项目&#xff0c;公告信息等…

机器学习 高维数据可视化:t-SNE 降维算法

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

NSSCTF靶场练习[HUBUCTF 2022 新生赛]

[HUBUCTF 2022 新生赛]simple_RE 签到题 一个base64编码&#xff0c;自定义了码表、 [HUBUCTF 2022 新生赛]ezPython py逆向&#xff0c;用在线网站反编译一下 先解一次base64&#xff0c;再解一次base58 接着再把 password转换成 bytes的形式做一次md5加密就好 from Crypto…

基于ASF-YOLO融合空间特征和尺度特征的新型注意力尺度序列融合模型开发构建医学场景下细胞分割检测识别系统,以【BCC、DSB2018数据集为基准】

作者提出了一种新的基于注意尺度序列融合的YOLO框架&#xff08;ASF-YOLO&#xff09;&#xff0c;该框架结合了空间和尺度特征&#xff0c;实现了准确快速的细胞实例分割。基于YOLO分割框架&#xff0c;我们使用尺度序列特征融合&#xff08;SSFF&#xff09;模块来增强网络的…

【Java代码审计】XSS篇

【Java代码审计】XSS篇 1.Java中XSS常见触发位置2.反射型XSS3.存储型XSS4.XSS漏洞修复 1.Java中XSS常见触发位置 XSS漏洞产生后必然会有相关的输入/输出&#xff0c;因此我们只需快速找到这些输入/输出点&#xff0c;即可快速地进行跟踪发现漏洞。输入在Java中通常使用“reque…

基于Java SSM框架实现疫情居家办公OA系统项目【项目源码+论文说明】

基于java的SSM框架实现疫情居家办公OA系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识…

基于循环神经网络长短时记忆(RNN-LSTM)的大豆土壤水分预测模型的建立

Development of a Soil Moisture Prediction Model Based on Recurrent Neural Network Long Short-Term Memory in Soybean Cultivation 1、介绍2、方法2.1 数据获取2.2.用于预测土壤湿度的 LSTM 模型2.3.土壤水分预测的RNN-LSTM模型的建立条件2.4.预测土壤水分的RNN-LSTM模型…

微信小程序置顶导航,替代原生导航栏

效果图&#xff1a; 思路&#xff1a;Navigation是小程序的顶部导航组件&#xff0c;当页面配置navigationStyle设置为custom的时候可以使用此组件替代原生导航栏&#xff0c;wx.getSystemInfoSync获取可使用窗口高度 wxml代码&#xff1a; <!-- 头部 --> <view cla…

【docker 】Compose 使用介绍

Docker Compose Docker Compose文档 Docker Compose GitHub地址 Docker Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose&#xff0c;您可以使用 YML 文件来配置应用程序需要的所有服务。然后&#xff0c;使用一个命令&#xff0c;就可以从 YML 文件配…

在React中实现好看的动画Framer Motion(案例:跨DOM元素平滑过渡)

前言 介绍 Framer Motion 是一个适用于 React 网页开发的动画库&#xff0c;它可以让开发者轻松地在他们的项目中添加复杂和高性能的动画效果。该库提供了一整套针对 React 组件的动画、过渡和手势处理功能&#xff0c;使得通过声明式的 API 来创建动画变得简单直观。 接下来…

modbus 通信协议介绍与我的测试经验分享

1、简介 Modbus 协议是一种通信协议&#xff0c;用于工业自动化系统中的设备间通信。该协议最初由 Modicon 公司开发&#xff0c;并于 1979 年发布。 Modbus 协议通过串行通信格式进行通信&#xff0c;在物理层上支持 RS-232、RS-422 和 RS-485 等多种通信方式。在协议层面&am…