面向对象编程私有类字段+get/set还是公共类字段?

Posted

技术标签:

【中文标题】面向对象编程私有类字段+get/set还是公共类字段?【英文标题】:Object-Oriented programming private class field + get / set or public class field? 【发布时间】:2015-04-17 05:53:22 【问题描述】:

我是一名初级开发人员(目前正在练习 Java),我对我的代码的正确性有一个疑问,下面是一个示例: 我正在用 Java 编写一个简单的 MMO 游戏表示,我有 2 个类(字符和拼写)。 Character 具有属性(mName、mHealth 和 mEnergy),Spell 类具有属性(mSpellName、mSpellCost、mSpellDamage)。而Spell类也有一个方法叫做execute,这里是代码

public void spellExecute(Character caster, Character target) 
caster.mEnergy -= this.spellCost;
target.mHealth -= this.spellDamage

这种结构意味着字符字段是公共的,可以直接访问,但是在某些示例中,我看到所有字段都必须是私有的,并且只能通过 get/set 方法访问。我的问题是:一般来说,哪种方式更正确?这对我很重要,因为我想写一个好的代码:)

【问题讨论】:

根据我的经验,Getter 和 setter 通常被认为是正确的,即使您不对 get/set 进行任何中间处理 Getter 和 setter 的优点是,如果在设置值后必须更新类,则无需更改设计。 公共属性是邪恶的。每个质量检查工具都会发现它。 Are getters and setters poor design? Contradictory advice seen 的可能重复项 Why use getters and setters?的可能重复 【参考方案1】:

通常,您会使用 get/set 方法,因为它们允许类通过这些方法控制访问

使用您的类的任何其他类,应该只能以您描述的方式访问和更改您的字段。

例如让我们看一个简单(过于简单化)的燃油泵

class Pump

    constant int PRICE_PER_LITRE = 100; //in pence

    private int litresDispensed;
    private bool nozzleUp;
    private int litresAvailable;
    private int price; //in pence

    construct()
    
        litresDispensed = 0;
        nozzleUp = false;
    

    startFuelling()
    
        nozzleUp = true;
    

    stopFuelling()
    
        nozzleUp = false;
    

    takeFuel(int litresTaken)
    
        if(nozzleUp = true)
        
            litresAvailable -= litresTaken;
            price += litresTaken * PRICE_PER_LITRE;
        
        else
        
            error("You must lift the nozzle before taking fuel!");
        
    

    getPrice()
    
        if(nozzleUp = true)
        
            error("You can't pay until you've finished fuelling! Please return the nozzle");
        
        else
        
            return price;
        
    

我们的最终获取方法对于确保在此人尝试付款之前完成其余交易非常重要。

如果我们允许直接访问价格,他们就可以在加油前完成!这会让他们偷走我们所有的燃料。

如上所示,get 方法保护字段免受外部影响。它仍然可以被操纵,但只能以我们希望允许它被操纵的方式进行。另请注意,此字段根本没有设置方法:我们不希望用户能够设置自己的价格,只有我们指定的价格!

如果您编写的 get/set 方法只返回和设置字段,没有任何验证或检查,那么您可以简单地将字段设为公开(或者,您需要决定是否应该直接访问该字段):也就是说,最好尽可能使用 get/set 方法,因为它允许您在将来添加验证而不会破坏代码。

【讨论】:

【参考方案2】:

您说得对,编写好的代码很重要,而且刚开始掌握面向对象编程可能有点困难。

在这种情况下,我建议将 spellExecute 移动到类似的方法,除了 Character 类:

public void didUseSpell(Spell spell) 
    this.mEnergy -= spell.spellCost;


public void wasHitBySpell(Spell spell) 
    this.mHealth -= spell.spellDamage;

在你的拼写执行方法中,你会调用:

public void spellExecute(Character caster, Character target) 
    caster.didUseSpell(this);
    target.wasHitBySpell(this);

一般来说,解决这个问题有很多不同的方法,它们在代码的简洁性和冗长性方面都各不相同。另一种解决方案是为受法术影响的字段创建getter and setter methods。

【讨论】:

spell.cost()spell.damage() 也许? 谢谢,非常有用的解决方案! 如果您对this关键字不熟悉,可以阅读更多相关信息here【参考方案3】:

Getter/setter 更好,因为它封装或隐藏了实际类为设置数据所做的工作。将构造函数设置为私有,并让它初始化默认值,然后用户可以调用set方法来设置变量。

【讨论】:

【参考方案4】:

具有私有字段的 Getter 和 setter 通常遵循设计选择。原因是,您可以保护您的变量免受来自使用您的 API 的客户端的无意更改。 考虑下面这个例子

public class Dummy
 public int age;

现在客户可以这样做了

new Dummy().age = -1

使用 setter 和 getter

public class Dummy
   private int age;
   public void setAge(int age)
      if (age < 0)
       throw new RuntimeException("you are not allowed to do this");
   
   ......

许多框架,例如 Hibernate、IBATIS 等。遵循这些命名约定。希望这能回答您的问题。

【讨论】:

【参考方案5】:

Getter 和 setter(Java bean)更好。它还提供封装功能。这对于隐藏数据很有用。

  private int id;

    public int getId() 
            return id;
    

    public void setId(int id) 
           this.id = id;
    

构造函数用于在创建对象时初始化值。但是如果要在对象创建后设置数据值,则必须调用 setter 行为而不是调用构造函数。

【讨论】:

【参考方案6】:

Getter 和 setter 是首选 - 毕竟,如果您不打算使用它的主要功能,为什么还要使用面向对象的语言?

在这种情况下使用 setter,您可以轻松地执行健全规则,而无需每个调用者都必须复制逻辑,例如- 在字符尝试中:

public void reduceMentalHealth(int val) 
  if(this.mHealth > val) 
    this.mHealth -= val;
   else 
    this.mHealth = 0;

如果没有设置器,您在更改字段的任何地方都需要此逻辑。你也可以在这个方法中包括检查角色是否戴着精神无敌戒指之类的东西:-)

【讨论】:

【参考方案7】:

警告

您混合了两个相关的问题。


问题:

[1] 我应该使用直接访问字段,还是通过访问方法(“getter”(s)和“setter”(s))访问字段。

[2] 在这两种情况下,应该应用哪些访问修饰符?


简短的快速回答

[1] 我应该使用直接访问字段,还是通过访问方法(“getter”(s)和“setter”(s))访问字段。

Go,“通过访问器方法访问字段”又名“getter (s) 和 setter (s)”。另一种方法没有错,这取决于你想做什么。

[2] 在这两种情况下,应该应用哪些访问修饰符?

如果您选择“普通字段”,请使用“公共”,除非有特定原因使用“私有”或“受保护”。如果您选择“访问者方法”,请对字段使用“受保护”,对访问者使用“受保护”或“公共”。


冗长乏味的扩展答案

[1] 我应该使用直接访问字段,还是通过访问方法(“getter”(s)和“setter”(s))访问字段。

Go,“通过访问器方法访问的字段”又名“getter (s) 和 setter (s)”。

另一种方法没错,看你想做什么。

但是,由于这些方法可以覆盖属性访问,因此允许通过方法添加、删除或更改更多功能。

我建议,为数据对象保留“普通字段”。

[2] 在这两种情况下,应该应用哪些访问修饰符?

如果您选择“普通字段”,请使用“公共”,除非有特定原因使用“私有”或“受保护”。

如果您选择“访问者方法”,请使用“受保护”作为字段,并使用“受保护”或“公共”作为访问者。

对访问者的字段应用“公共”访问权限不是一个好主意,因为这样会混淆自己和类的其他程序员用户, 直接使用哪一个。

代码示例:

public class ControlClass 
    // example of non private field (s)
    protected String controlname;

    // ...

    // example of non private field (s)
    protected int width;
    protected int height;

    // ...

    // example of read-only property
    public final String getControlName()
    
        return this.controlname;
     // String getControlName(...)

    // ...

    @Override
    public int getWidth()
    
        return this.width;
     // int getWidth(...)

    @Override 
    public void setWidth(int newvalue)
    
        this.width = newvalue;
     // void setWidth(...)

    @Override
    public int getHeight()
    
        return this.height;
     // int getHeight(...)

    @Override 
    public void setHeight(int newvalue)
    
        this.height = newvalue;
     // void setHeight(...)

    // ...

    // constructor assigns initial values to properties' fields
    public ControlClass()
        this.controlname = "control";
        this.height = 0;
        this.width = 0;
     // ControlClass(...)

    // ...
 // class ControlClass

public class ButtonClass extends ControlClass 
    // ...

    // example of overriden public property with non public field
    @Override
    public int getWidth()
    
        if (this.width < 5)
          return 5
        else
          return this.width;
     // int getWidth(...)

    // example of overriden public property with non public field
    @Override 
    public void setWidth(int newvalue)
    
        if (newvalue < 5)
          throw new ArithmeticException("integer underflow");

        this.width = newvalue;
     // void setWidth(...)

    // example of overriden public property with non public field
    @Override
    public int getHeight()
    
        if (this.height < 5)
          return 5
        else
          return this.height;
     // int getHeight(...)

    // example of overriden public property with non public field
    @Override 
    public void setHeight(int newvalue)
    
        if (newvalue < 5)
          throw new ArithmeticException("integer underflow");

        this.height = newvalue;
     // void setHeight(...)

    // ...

    // constructor assigns initial values to properties' fields
    public ControlClass()
        this.controlname = "ButtonClass";
        this.height = 5;
        this.width = 5;
     // ButtonClass(...) 

    // ...
 // class ControlClass

我开始对字段使用“private”,像许多程序员一样,购买,最终不得不更改为“protected”,因为有时需要使用它。直接。


附加评论。

我曾使用过其他面向对象的编程语言,它们有自己的“属性”方式和语法,这为您提供了另一个视角。

例如 Object Pascal(又名“Delphi”)、ECMAScript(“javascript”)、C++、C#。请注意,Delphi 和 C# 支持“完整属性”,而不仅仅是访问方法或字段,这为开发人员提供了另一种设计面向对象和类的软件应用程序的方式。

这和 Java 有什么关系?

当我用 Java 或 C++ 设计一个类时,我会设计属性,就像 C# 或 Delphi 一样,一个独立于字段或方法的概念,即使可以由他们实施。


干杯。

【讨论】:

以上是关于面向对象编程私有类字段+get/set还是公共类字段?的主要内容,如果未能解决你的问题,请参考以下文章

我应该使用公共属性和私有字段还是公共字段来存储数据?

面向对象总结

javascript 仿面向对象编程实例代码(私有,公共变量。。。)

面向对象——三大特性

Java面向对象编程思想

面向对象编程中啥是公共的、私有的和受保护的?