在Java中,为何要用接口interface和实现类implements

 在最开始的时候,大家可能都会想,我们习惯于直接把业务代码写在Controller中多方便;后面出现MVC框架,我们就把业务代码写在Service中;现在为何还要先写个接口,再麻烦的写一个实现类?我最开始学习的时候也是有这个疑惑,下面给大家解答疑惑。

在Java中,使用implements关键字来实现接口是一种面向接口编程的重要实践,它带来了以下几方面的好处:

  1. 松耦合:接口定义了服务提供者应遵循的规范,而具体实现与接口分离。这样,当服务的实现需要改变时(比如从MySQL迁移到MongoDB),只要修改实现类而不影响到依赖该接口的其他模块,提高了系统的灵活性和可维护性。

  2. 多实现:一个接口可以有多个实现类,每个实现类可以根据不同需求提供不同的实现方式。这为程序设计提供了高度的灵活性和扩展性。

  3. 标准化编程:接口定义了一组公共的行为标准,鼓励遵循统一的编程规范,使得代码更加规范和易于理解。对于大型项目或团队开发,这一点尤为重要,有助于保持代码风格的一致性。

  4. 利于测试和 mock:在进行单元测试时,可以轻松地使用Mock对象替换真实的实现,因为测试代码只依赖于接口,而不是具体的实现细节。这使得测试更加聚焦,也更容易构造和控制测试环境。

  5. 设计先行:在实际编码之前设计接口,可以帮助开发者在更高层次上思考系统架构和组件间的交互,促进良好的软件设计。接口定义了“应该做什么”,而非“如何做”,有助于清晰地划分职责。

  6. 提高代码重用性:接口作为抽象层,可以在不同的项目或模块中重用,减少重复代码,提高代码的复用率。

因此,虽然直接编写Service类看起来可能更直接简单,但通过定义接口并实现它,能够为长期的项目维护、扩展和团队协作带来显著的优势。

举个简单的例子说明一下:

假设我们正在开发一个用户管理系统,其中有一个需求是能够保存用户信息到数据库中。按照面向接口编程的原则,我们可以这样做:

首先,定义一个用户服务接口 (UserService.java):

public interface UserService {
    void saveUser(User user);
}

在这个接口中,我们声明了一个方法 saveUser,用于保存用户信息。接口不包含任何具体的实现,它只是定义了一个服务应当提供的功能。

然后,创建这个接口的一个实现类 (UserServiceImpl.java):

import javax.annotation.Resource;

@Service // Spring注解,表示这是一个Service Bean
public class UserServiceImpl implements UserService {

    @Resource // 假设使用Spring框架,注入UserDao实例
    private UserDao userDao;

    @Override
    public void saveUser(User user) {
        // 实现保存用户到数据库的逻辑
        userDao.insert(user);
    }
}

在这个实现类中,我们实现了 UserService 接口中的 saveUser 方法,并提供了具体的数据库操作逻辑。

现在,在其他需要使用用户保存功能的组件中,我们只需要依赖 UserService 接口,而不是具体的实现类。这样做的好处如下:

  • 灵活性:如果未来需要更换数据库存储方式或添加额外的逻辑(例如,发送注册成功邮件),我们只需要修改 UserServiceImpl 类,而调用 UserService.saveUser 的地方无需改动。
  • 可测试性:在单元测试中,我们可以轻松地为 UserService 提供一个模拟实现(Mock对象),以便于测试业务逻辑,而不需要真实操作数据库。
  • 多实现支持:如果有多种用户存储的需求(比如同时支持数据库存储和云端存储),我们可以为 UserService 接口提供多个实现类,系统可以根据需要选择合适的实现。

通过这个例子,可以看到使用接口和实现类的方式增强了代码的可维护性、灵活性和可测试性。

多个实现类的例子:

让我们基于前面的用户服务例子,展示如何通过多个实现类来支持不同的用户存储策略。假设我们现在除了要将用户信息保存到数据库外,还想支持将用户信息保存到云端存储(例如:云数据库或文件存储服务)。我们可以这样做:

首先,保持原有的 UserService 接口不变:

public interface UserService {
    void saveUser(User user);
}

接着,保留原有的数据库实现类 UserServiceImpl

@Service("dbUserService")
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    @Override
    public void saveUser(User user) {
        userDao.insert(user);
    }
}

然后,新增一个云端存储的实现类 CloudUserService.java

@Service("cloudUserService")
public class CloudUserService implements UserService {

    private final CloudUserRepository cloudUserRepository; // 假设这是一个操作云端数据库的客户端

    @Autowired
    public CloudUserService(CloudUserRepository cloudUserRepository) {
        this.cloudUserRepository = cloudUserRepository;
    }

    @Override
    public void saveUser(User user) {
        // 实现将用户信息保存到云端的逻辑
        cloudUserRepository.save(user);
    }
}

这里,CloudUserService 实现了 UserService 接口,但是它的 saveUser 方法将用户信息保存到了云端存储中,而不是本地数据库。

最后,在需要使用用户保存服务的地方,根据实际需求选择不同的实现。可以通过Spring的依赖注入(@Autowired)并使用@Qualifier注解来指定具体的服务实现:

@Autowired
@Qualifier("dbUserService")
private UserService dbUserService;

@Autowired
@Qualifier("cloudUserService")
private UserService cloudUserService;

或者,如果你的应用场景允许动态选择服务实现,可以通过工厂模式或策略模式进一步封装这个选择过程。

通过这种方式,我们的系统变得更加灵活,可以根据不同的需求或环境条件轻松切换用户信息的存储策略,而无需修改大量现有代码。这就是使用多个实现类所带来的优势之一。

共同勉励,一起学习,加油。

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值