حالت های مختلف برابری دو Object در دنیای برنامه نویسی!!!

سلام دوستان.

توی این مقاله ی کوچولو قراره چنتا نکته جذاب باهم یاد بگیریم.

یه کلاسی داریم, اسمشو هر چی دوست داری بذار….من میذارم User.

class User{
    private int id;
    private String firstname;
    private String lastname;
    private boolean isAdmin;
    
     public User(String firstname, String lastname, boolean isAdmin){
        this.id = generateUniqueId();
        this.firstname = firstname;
        this.lastname = lastname;
        this.isAdmin = isAdmin;
    }
    
    private static int generateUniqueId(){
        .....
    }
}

حالا جاهای مختلف برنامه ممکنه Object های مختلفی از این کلاس بسازیم.

var admin = new User('Admin', 'Admin Zadeh', true);

var user = new User('Karbar', 'Mohtaram', false);

و خب تعداد این Object ها میتونه بیشتر و بیشتر باشه.

و حالا فرض کن که میخوای این Object های رو با هم مقایسه کنی .

این مقایسه توی زبان های مختلف ممکنه به روش های متفاوت انجام بشن.

مثلن توی بعضی زبان ها باید از متد equals استفاده کنی و اون روی object مورد نظر call کنی مثل C# یا JAVA.

if(admin.equals(user)){
    //do your work
}

توی این حالت برای اینکه عملکرد رو تغییر بدی باید بری و متد equals رو داخل کلاس موردنظر override کنی.

توی بعضی زبان ها میتونی از operator ها استفاده کنی (این جا ==) مثل Dart.

if(admin == user)){
    //do your work
}

و این زبان ها این قابلیت رو بهت میدن که داخل کلاس ها بتونی operator ها رو override کنی و عملکردشون رو تغییر بدی.

 

پس فرقی نمیکنه با چه زبانی کار کنیم, در هر صورت برای تغییر عملکرد متد مقایسه برابری دو Object باید بریم و یه چیزی رو داخل کلاس مورد نظر override کنیم حالا یا متد equals یا operator == یا … و مقدار return type همه این ها هم یه boolean خواهد بود که مشخص میکنه آیا دو تا Object مورد نظر با هم برابرند یا نه.

 

حالا بریم حالت های مختلفی که میتونه اتفاق بیوفته رو با هم بررسی کنیم.

 

اولش بگم که وقتی که از یه کلاس یه Object جدید میسازیم این Object یه فضایی رو توی حافظه به خودش اختصاص میده و یه خونه از حافظه رو اشغال میکنه (ram).

مثلن من میگم :‌

var user = new User('Karbar', 'Mohtaram', false);

این جا با کلید new یه Object ساخته میشه و توی حافظه (هیپ) ذخیره میشه و ادرس اون Object توی هیپ, توی یه variable به نام user ذخیره میشه که خودش توی استک ذخیره میشه.

صبر کن صبر کن…ببین فرض کن خیلی ساده حافظه رو به دو قسمت تقسیم کردیم یکی هیپ و یکی استک.

در مورد این ها که چی هستن و چه جوری عمل میکنن بعدن توضیح میدم.

ولی الان همین رو بدون که توی جمله بالا برای user (کوچولو- سمت چپ ) یه چیزی توی استک ساخته میشه و داخلش یه ادرس ذخیره میشه.

از اون طرف برای new User… یه Object ساخته میشه و توی هیپ ذخیره میشه و ادرس مکانی که این Object ذخیره شده داده میشه به اون user که توی استک هست.

همین قدر فعلن بدون کافیه…اگرم خود بیشتر میدونی که چه عالی.

حالا فرض کن که من این رو دارم :‌

var user = new  User('Karbar', 'Mohtaram', false);
user = new  User('Karbar', 'Na Mohtaram', false)

توی خط اول یه object از کلاس User ساختم و ریختمش توی user.

توی خط دوم یه object دیگه ساختم و بازم ریختمش توی همون user قبلی.

اینجا دو تا new داریم پس دو تا object ساخته شده و این دو تا توی دو تا خونه مختلف از حافظه ذخیره شدن(هیپ).

مثلن خونه اولی خونه شماره 101 هست و دومی شماره 102.

حالا اون user فقط داره این آدرس ها رو توی خودش نگه میداره و توی خط اول 101 رو توی خودش نگه میداره و اشاره میکنه به خونه شماره 101 از حافظه و توی خط دوم این آدرس تعییر میکنه و 102 رو بهش میدیم و از این به بعد خانم یا آقای user اشاره میکنه به خونه 102 از حافظه.

پس همچنان دو تا object توی حافظه داریم و اون user فقط آدرس هاشون رو نگه میداره و توی خط دوم که آدرس Object دوم رو دادیم به user, در واقع از اینجا به بعد object اولی بی صاحاب میشه و هیچکسی آدرسشو نگه نمیداره و همین جوری بی صاحاب برای خودش توی حافظه میمونه تا اینکه آشغال جمع کنی بیاد و جمعش کنه (زباله بی مصرف).

 

خیلی حرف زدم. دو ساعته دارم چرت و پرت میگم.

بریم سر اصل مطلب و حالت های مختلف مقایسه دو object.

 

حالت اول:‌ Reference equality

توی این حالت دو تا var باید دقیقن به یه خونه از حافظه اشاره کنن که نتیجه مقایسه برابری اون ها true بشه.

مثلن کلاس User رو در نظر بگیر :‌

class User{
    public int id;
    public String firstname;
    public String lastname;
    public boolean isAdmin;
    
    public User(String firstname, String lastname, boolean isAdmin){
        this.id = generateUniqueId();
        this.firstname = firstname;
        this.lastname = lastname;
        this.isAdmin = isAdmin;
    }
    
    private static int generateUniqueId(){
        .....
    }
}

حالا :

var user = new User('Karbar', 'Mohtaram', false);

var user1 = new User('Karbar', 'Mohtaram', false);

با وجود اینکه هر دو دقیقن یه ساختار دارن ولی نتیجه مقایسه دو تا variable بالا false هست.

چرا ؟

خودت بگو, دیگه زشته من بگم.

حالت پیش فرض متد equals یا … توی زبان های برنامه نویسی هم این حالت هست. یعنی اگه خودت نری و دست کاریش نکنی, حالت پیش فرض فقط چک میکنه که این دو تا بچه تو یه خونه نشسته باشن.

به این حالت میگن که دو تا object مورد نظر identical هستن.

 

حالت دوم: Structural equality

توی این حالت ساختار واسمون مهمه و مهم نیست که الزامن دو تا var به یه خونه اشاره کنن.

مثلن

var user = new User('Karbar', 'Mohtaram', false);

var user1 = new User('Karbar', 'Mohtaram', false);

چون این دو طفل معصوم دقیقن ساختار یکسانی دارن, با اینکه دارن به آدرس های مختلف اشاره میکنن ولی باز هم نتیجه مقایسه برابری اون ها باید true برگردونه.

برای این منظور دیگه باید بریم و متد equals یا هر چیز دیگه ای (مطابق با زبان برنامه نویسی, البته اگه زبانی که کار میکنید واقعن زبان باشه و از این زبان های تخیلی نباشه) رو override کنیم و مطابق میل خودمون بنویسیمش.

class User{
    private int id;
    private String firstname;
    private String lastname;
    private boolean isAdmin;
    
  public User(String firstname, String lastname, boolean isAdmin){
        this.id = generateUniqueId();
        this.firstname = firstname;
        this.lastname = lastname;
        this.isAdmin = isAdmin;
    }
    
    private static int generateUniqueId(){
        .....
    }
    
    public boolean equals(Object other){
      if (other == this) {  
         return true;  
      }
      
      if (!(o instanceof User)) {  
         return false;  
      }
      
      var o = (user) other;
      
      if(id != o.id){
          return false;
      }
      
      if(firstname != o.firstname){
          return false;
      }
      
      if(lastname != o.lastname){
          return false;
      }
      
      if(isAdmin != o.isAdmin){
          return false;
      }
      
      return true;
    }
}
    
     

توی خط 19 بررسی میکنیم که آیا این دو تا دقیقن به یه آدرس اشاره میکنن یا نه و اگه آره مستقیم و بدون کش دادن قضیه true برمیگردونیم.

برای این منظور توی زبان های مختلف ممکنه روش های مختلفی استفاده بشه. مثلن توی زبان Dart برای اینکه بفهمیم دو تا var دقیقن به یه آدرس اشاره میکنن باید از یه function به نام identical استفاده کنیم.

در ادامه (خط 19 به بعد) هم که خیلی واضحه و ساختار دو تا Object رو با هم مقایسه میکنیم و اگه یکی بودن true برمیگردونیم و اگه کوچکترین تفاوتی داشتن false.

 

حالت سوم: Identifier equality

خیلی هاتون فکر کردین فقط همون دو حالته.

آره؟

راستشو بگو…

حالت سوم حالت Identifier equality هست. توی این حالت هویت دو object برامون مهم هست. این که دو تا var به آدرس های مختلف اشاره کنن یا حتا ساختار های مختلف داشته باشن مهم نیست. همین که هویت یکسان داشته باشن, نتیجه باید true باشه.

حالا میگی هویت چیه؟

معمولن Id یا هر چیزی که نشون دهنده هویت باشه.

کلاس User رو دوباره ببین:

class User{
    private int id;
    private String firstname;
    private String lastname;
    private boolean isAdmin;
    
    public User(int id, String firstname, String lastname, boolean isAdmin){
        this.id = id;
        this.firstname = firstname;
        this.lastname = lastname;
        this.isAdmin = isAdmin;
    }
}

حالا :

var admin = new User(100و 'Admin', 'Admin Zadeh', true);
var user = new User(100و 'Karbar', 'Mohtaram', false);

این دو تا یعنی admin و user هم به آدرس های مختلفی اشاره میکنن پس از نظر روش اول برابر نیست و هم ساختار متفاوت دارن پس از نظر روش دوم هم برابر نیستن.

اما اما اما … id های یکسان دارن. پس از نظر روش سوم با هم برابرن.

مثلن یه سایتی رو در نظر بگیر که یه دیتابیسی داره و کاربران با id داخل دیتابیس از هم تفکیک میشن.

حالا ممکنه تو امروز ادمین باشی و فردا بزنن تو سرت و بگن دیگه ادمین نیستی یا بری اسم و رسمت رو ویرایش کنی.

ولی تو تغییر که نکردی…تو همونی… هویتت همونه… و هنوز همون ردیف از دیتابیس رو اشغال کردی.

حالا برای پیاده سازی این هم باید بریم و متد equals یا … (مثلن operator == توی Dart ) رو override کنیم.

چه جوری؟‌

اول خودت فکر کن بعد ببین :‌

class User{
    private int id;
    private String firstname;
    private String lastname;
    private boolean isAdmin;
    
    public User(int id, String firstname, String lastname, boolean isAdmin){
        this.id = id;
        this.firstname = firstname;
        this.lastname = lastname;
        this.isAdmin = isAdmin;
    }
    
    public boolean equals(Object other){
      if (other == this) {  
         return true;  
      }
      
      if (!(o instanceof User)) {  
         return false;  
      }
      
      var o = (user) other;
      
      return id == o.id;
    }
}

بله فقط id ها رو مقایسه میکنیم.

 

خب داستان امروزمون تموم شد, خیلی یهویی هم تموم شد.

امیدوارم که لذت برده باشید.

دیدگاهتان را بنویسید

error: Alert: Content is protected !!