【封装丨优雅方法】

封装是个老生常谈的话题了
那么如何优雅的封装呢?
不要急
本文下面就讲一讲优雅封装的几种方式

一:封装的含义:

  封装是一种面向对象编程的概念,指将一个类的内部数据和方法进行保护,使其不能被外部直接访问和修改,而只能通过类的公共方法进行操作。这样可以提高代码的安全性和可维护性。
 封装可以分为两个方面:

  1. 数据封装,即限制外部访问类的私有数据成员,只能通过类的公有接口进行访问;
  2. 行为封装,即限制外部直接调用类的内部函数,只能通过类的公有接口进行访问。

二:优雅的封装

1. 使用接口和抽象类

  对于一些通用的方法或者属性,可以将其定义为接口或者抽象类。这样可以让子类继承并实现这些方法或属性,从而提高代码的灵活性和可扩展性。
实例:


public interface Shape {
    void draw();
}

public abstract class ShapeWithDrawMethod {
    public abstract void draw();
}

public class Circle extends ShapeWithDrawMethod implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

public class Square extends ShapeWithDrawMethod implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a square");
    }
}

public class ShapeFactory {
    public static Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        } else if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        } else {
            throw new IllegalArgumentException("Invalid shape type");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = ShapeFactory.getShape("CIRCLE");
        if (circle != null) {
            circle.draw(); // 调用Circle类中的draw方法,会输出"Drawing a circle"
        } else {
            System.out.println("Invalid shape type");
        }
    }
}

 在这个示例中,我们定义了一个 Shape 接口和两个实现了该接口的类 Circle 和 Square。同时,我们还定义了一个 ShapeFactory 类,用于根据传入的参数类型返回对应的实例对象。在 Main 类中,我们通过调用 ShapeFactory.getShape() 方法获取一个 Shape 对象,并判断是否为空。如果不为空,则调用其 draw()。

2. 使用私有变量和公有方法

  将需要保护的数据放在私有变量中,只提供公有方法供外部调用。这样可以避免外部直接访问和修改私有变量,保证了数据的安全性和封装性。


public class Shape {
    private String name;
    
    public Shape(String name) {
        this.name = name;
    }
    
    public void draw() {
        System.out.println("Drawing " + name);
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("Circle");
        circle.draw(); // 输出"Drawing Circle"
    }
}

 在这个示例中,我们定义了一个 Shape 类,其中包含了一个私有变量 name 和一个公有方法 draw()。在构造方法中,我们将传入的参数赋值给私有变量。这样可以保证数据的安全性和封装性。同时,只提供了公有接口,外部无法直接访问私有变量,从而保证了代码的可维护性和安全性。

3. 使用静态方法和常量

  对于一些不需要访问实例变量的方法,可以使用静态方法或者常量来实现。这样可以避免在每个实例化的对象中都创建相同的方法或变量,提高了代码的复用性和可维护性。

以下是一个使用静态方法和常量封装的示例:


public class Shape {
    private String name;
    
    public Shape(String name) {
        this.name = name;
    }
    
    public static final String CIRCLE_NAME = "Circle";
    
    public static void draw() {
        System.out.println("Drawing " + CIRCLE_NAME);
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("Circle");
        Shape.draw(); // 输出"Drawing Circle"
    }
}


  在这个示例中,我们定义了一个 Shape 类,其中包含了一个私有变量 name。同时,我们定义了一个静态常量 CIRCLE_NAME,用于表示圆形的名称。在静态方法 draw() 中,我们直接使用了静态常量 CIRCLE_NAME,而不需要创建任何实例对象。这样可以避免重复创建对象,提高了代码的效率和可维护性。同时,由于静态方法是属于类本身的,而不是属于类的实例的,因此可以直接通过类名调用,而不需要创建实例对象。

4. 使用注解

  Java 中的注解可以用于描述类、方法、字段等元素的行为和属性。通过注解,可以在编译时或者运行时获取元素的信息,从而实现更加灵活和高效的封装。

以下是一个使用注解封装的示例:


public class Shape {
    private String name;
    
    public Shape(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Shape{name=" + name + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("Circle");
        System.out.println(circle); // 输出"Shape{name=Circle}"
    }
}


  在这个示例中,我们定义了一个 Shape 类,其中包含了一个私有变量 name。同时,我们使用了 Java 中的注解 @Override,用于覆盖父类 Object 中的 toString() 方法。在 toString() 方法中,我们返回了一个字符串,用于表示当前对象的信息。这样可以方便地通过 System.out.println() 方法输出对象的信息,而不需要手动编写打印代码。同时,由于注解是属于类本身的,而不是属于类的实例的,因此可以直接通过类名调用,而不需要创建实例对象。

5.使用枚举类型

  对于一些需要限定取值范围的变量,可以使用枚举类型来实现。这样可以避免使用整型等数据类型可能出现的溢出和错误,同时也可以提高代码的可读性和可维护性。

以下是一个使用枚举类型封装的示例:


public enum Shape {
    ROUND, CIRCLE;
    
    public static Shape getShapeByName(String shapeName) {
        for (Shape shape : values()) {
            if (shape.name().equalsIgnoreCase(shapeName)) {
                return shape;
            }
        }
        throw new IllegalArgumentException("Invalid shape name: " + shapeName);
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = Shape.CIRCLE;
        System.out.println(Shape.getShapeByName("Circle")); // 输出"Circle"
    }
}

  在这个示例中,我们定义了一个 Shape 枚举类型,其中包含了两个枚举值 ROUNDCIRCLE。同时,我们还定义了一个静态方法 getShapeByName(),用于根据传入的名称参数返回对应的枚举值。在方法中,我们使用了 for-each 循环遍历所有的枚举值,并通过 equalsIgnoreCase() 方法进行比较。如果找到了匹配的枚举值,则直接返回该值。否则,抛出一个 IllegalArgumentException 异常。这样可以保证数据的安全性和封装性,同时提供了更好的类型检查和错误提示。

6. 其他

6.1 多种方式联合封装

 1.使用访问控制符:
  Java中有public、private和protected等访问控制符,可以限制属性和方法的访问权限。一般来说,属性都应该使用private访问控制符,方法可以根据需要使用public、private或protected。
 2.使用getter和setter方法 :
  将属性设置为private,然后提供public的getter和setter方法,使得外部可以访问对象的属性,但是不能直接修改它们。同时,可以在getter和setter方法中添加一些控制逻辑,如检查属性的合法性。
 3.使用final关键字 :
  使用final关键字可以将属性和方法设置为不可变的,这可以避免外部对对象进行不必要的修改,提高代码的安全性和可维护性。
示例:


public class Shape {
    private final String name;
    
    public Shape(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        throw new IllegalArgumentException("Cannot modify the shape name");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("Circle");
        System.out.println(circle.getName()); // 输出"Circle"
        try {
            circle.setName("Square"); // 抛出异常,因为setName方法被final修饰了
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage()); // 输出"Cannot modify the shape name"
        }
    }
}

  在这个示例中,我们定义了一个 Shape 类,其中包含了一个私有变量 name,并且使用了 final 关键字进行修饰。同时,我们使用了访问控制符 publicprivategetset 分别控制了公有、私有、getter、setter 方法的访问权限。这样可以保证数据的安全性和封装性,同时提供了更好的类型检查和错误提示。同时防止了意外修改私有变量的值。由于 final 关键字修饰的方法不能被重新定义或调用,因此可以避免了一些潜在的问题。

6.2 使用包装类

  如果需要封装一些原始类型或者复杂类型的数据,可以使用Java提供的包装类,如Integer、Double、List等,通过包装类暴露一些公共接口,隐藏底层实现细节。

使用包装类封装的好处包括:

  1. 提高代码的可读性和可维护性。包装类可以隐藏内部实现细节,只暴露出必要的接口,使得代码更加清晰易懂。
  2. 增加代码的灵活性和扩展性。通过在包装类中添加新的功能或修改已有的功能,可以不影响原有的接口和代码逻辑。
  3. 保护内部实现不被直接访问。将内部实现封装在包装类中,可以防止外部代码直接访问和修改内部状态,从而提高系统的安全性。
  4. 支持多态性。通过包装类可以将对象的不同实现方式统一起来,从而支持多态性,使得代码更加灵活和易于扩展。
  5. 可以对数据进行验证和处理。在包装类中可以添加数据验证和处理的方法,从而保证数据的正确性和完整性。

以下是一个使用包装类封装的示例:


public class Shape {
    private String name;
    
    public Shape(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        throw new IllegalArgumentException("Cannot modify the shape name");
    }
}

public class ShapeWrapper implements Shape {
    private final Shape innerShape;
    
    public ShapeWrapper(String name) {
        innerShape = new Shape(name);
    }
    
    @Override
    public String getName() {
        return innerShape.getName();
    }
    
    @Override
    public void setName(String name) throws IllegalArgumentException {
        innerShape.setName(name);
    }
}

public class Main {
    public static void main(String[] args) {
        ShapeWrapper circle = new ShapeWrapper("Circle");
        System.out.println(circle.getName()); // 输出"Circle"
        try {
            circle.setName("Square"); // 抛出异常,因为setName方法被final修饰了
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage()); // 输出"Cannot modify the shape name"
        }
    }
}

  在这个示例中,我们定义了一个 Shape 类和一个 ShapeWrapper 类。其中,ShapeWrapper 类继承自 Shape 类,并将 Shape的 get, set 方法重写。降低了耦合,提高了代码的复用性。

总结:

  合理的封装可以提高代码的质量和效率,使得代码更加易于维护和扩展,但是切忌不要过度封装嗷!

在这里插入图片描述

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

家有娇妻张兔兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值