SOLID በመጠቀም ተለዋዋጭ ኮድ እንጽፋለን

SOLID በመጠቀም ተለዋዋጭ ኮድ እንጽፋለን

ከአስተርጓሚው፡- ለእርስዎ የታተመ ጽሑፍ በ Severin Perez በፕሮግራም አወጣጥ ውስጥ የ SOLID መርሆዎችን ስለመጠቀም። ከጽሁፉ ውስጥ ያለው መረጃ ለጀማሪዎች እና ልምድ ላላቸው ፕሮግራመሮች ጠቃሚ ይሆናል.

ልማት ላይ ከሆንክ ስለ SOLID መርሆዎች ሰምተህ ይሆናል። የፕሮግራም አድራጊው ንጹህ፣ በሚገባ የተዋቀረ እና በቀላሉ ሊጠበቅ የሚችል ኮድ እንዲጽፍ ያስችላሉ። በፕሮግራም ውስጥ አንድን ሥራ በትክክል እንዴት ማከናወን እንደሚቻል በርካታ አቀራረቦች መኖራቸውን ልብ ሊባል ይገባል። የተለያዩ ስፔሻሊስቶች ስለ "ትክክለኛው መንገድ" የተለያዩ ሀሳቦች እና ግንዛቤ አላቸው, ሁሉም በእያንዳንዱ ሰው ልምድ ላይ የተመሰረተ ነው. ነገር ግን፣ በSOLID ውስጥ የታወጁት ሃሳቦች በሁሉም የአይቲ ማህበረሰብ ተወካዮች ተቀባይነት አላቸው። ለብዙ መልካም ልማት አስተዳደር ተግባራት መፈጠርና መዳበር መነሻ ሆኑ።

የ SOLID መርሆዎች ምን እንደሆኑ እና እንዴት እንደሚረዱን እንረዳ።

Skillbox ይመክራል፡ ተግባራዊ ኮርስ "ሞባይል ገንቢ PRO".

እኛ እናስታውስዎታለን- ለሁሉም የ "ሀብር" አንባቢዎች - የ "Habr" የማስተዋወቂያ ኮድን በመጠቀም በማንኛውም የ Skillbox ኮርስ ውስጥ ሲመዘገቡ የ 10 ሩብልስ ቅናሽ.

SOLID ምንድን ነው?

ይህ ቃል ምህጻረ ቃል ነው፣ እያንዳንዱ የቃሉ ፊደል የአንድ የተወሰነ መርህ ስም መጀመሪያ ነው።

  • Sኢንግል የኃላፊነት መርህ። አንድ ሞጁል ለለውጥ አንድ እና አንድ ምክንያት ብቻ ሊኖረው ይችላል.
  • Oብዕር/የተዘጋ መርህ (ክፍት/የተዘጋ መርህ)። ክፍሎች እና ሌሎች ንጥረ ነገሮች ማራዘሚያ ክፍት መሆን አለባቸው, ነገር ግን ለማሻሻል ዝግ መሆን አለባቸው.
  •  የ Liskov የመተካት መርህ (ሊስኮቭ የመተካት መርህ). የመሠረት ዓይነትን የሚጠቀሙ ተግባራት ሳያውቁት የመሠረት ዓይነት ንዑስ ዓይነቶችን መጠቀም መቻል አለባቸው።
  • Iየበይነገጽ መለያየት መርህ  (በይነገጽ መለያየት መርህ). የሶፍትዌር አካላት በማይጠቀሙባቸው ዘዴዎች ላይ ጥገኛ መሆን የለባቸውም.
  • Dኢፔንደንስ ኢንቬንሽን መርህ (የጥገኛ መገለባበጥ መርህ)። በከፍተኛ ደረጃ ላይ ያሉ ሞጁሎች በዝቅተኛ ደረጃዎች ላይ ባሉ ሞጁሎች ላይ የተመካ መሆን የለባቸውም.

ነጠላ የኃላፊነት መርህ


ነጠላ ሃላፊነት መርህ (SRP) በፕሮግራሙ ውስጥ ያለው እያንዳንዱ ክፍል ወይም ሞጁል ለፕሮግራሙ ተግባር አንድ ክፍል ብቻ ተጠያቂ መሆን እንዳለበት ይገልጻል። በተጨማሪም፣ የዚህ ኃላፊነት አካላት በማይዛመዱ ክፍሎች ውስጥ ከመበተን ይልቅ ለራሳቸው ክፍል መመደብ አለባቸው። የኤስአርፒ ገንቢ እና ዋና ወንጌላዊ ሮበርት ኤስ ማርቲን ተጠያቂነትን የለውጥ ምክንያት አድርጎ ይገልፃል። እሱ በመጀመሪያ ይህንን ቃል ከሥራው አካል ውስጥ እንደ አንዱ ሀሳብ አቅርቧል "የነገር ተኮር ንድፍ መርሆዎች"። ጽንሰ-ሐሳቡ ቀደም ሲል በቶም ዴማርኮ የተገለፀውን አብዛኛው የግንኙነት ንድፍ ያካትታል።

ፅንሰ-ሀሳቡ በዴቪድ ፓርናስ የተቀመሩ በርካታ ፅንሰ ሀሳቦችንም አካቷል። ሁለቱ ዋናዎቹ ኢንካፕሌሽን እና የመረጃ መደበቅ ናቸው። ፓርናስ ስርዓቱን ወደ ተለያዩ ሞጁሎች መከፋፈል በብሎክ ስዕላዊ መግለጫዎች ወይም በአፈፃፀም ፍሰቶች ላይ የተመሠረተ መሆን የለበትም ሲል ተከራክሯል። ማንኛውም ሞጁሎች ለደንበኞች በትንሹ መረጃ የሚሰጥ የተወሰነ መፍትሄ መያዝ አለባቸው።

በነገራችን ላይ ማርቲን ከአንድ ኩባንያ ከፍተኛ አስተዳዳሪዎች (COO, CTO, CFO) ጋር አንድ አስደሳች ምሳሌ ሰጠ, እያንዳንዳቸው የተወሰኑ የንግድ ሶፍትዌሮችን ለተለያዩ ዓላማዎች ይጠቀማሉ. በውጤቱም, አንዳቸውም ቢሆኑ የሌላ አስተዳዳሪዎችን ፍላጎት ሳይነኩ በሶፍትዌሩ ላይ ለውጦችን መተግበር ይችላሉ.

መለኮታዊ ነገር

እንደ ሁልጊዜው፣ SRP ለመማር ምርጡ መንገድ በተግባር ማየት ነው። ነጠላ የኃላፊነት መርህን የማይከተል የፕሮግራሙን ክፍል እንይ። ይህ የጠፈር ጣቢያውን ባህሪ እና ባህሪያት የሚገልጽ የሩቢ ኮድ ነው።

ምሳሌውን ይገምግሙ እና የሚከተሉትን ለመወሰን ይሞክሩ:
በ SpaceStation ክፍል ውስጥ የታወጁት የእነዚያ ነገሮች ሀላፊነቶች።
በጠፈር ጣቢያው አሠራር ላይ ፍላጎት ያላቸው.

class SpaceStation
  def initialize
    @supplies = {}
    @fuel = 0
  end
 
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
 
  def report_supplies
    puts "----- Supply Report -----"
    if @supplies.keys.length > 0
      @supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def report_fuel
    puts "----- Fuel Report -----"
    puts "#{@fuel} units of fuel available."
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    if @fuel >= 10
      puts "Thrusting action successful."
      @fuel -= 10
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end

እንደ እውነቱ ከሆነ የእኛ የጠፈር ጣቢያ የማይሰራ ነው (በቅርቡ ከናሳ የሚደውልልኝ አይመስለኝም) ግን እዚህ የምንተነትነው ነገር አለ።

ስለዚህ የSpaceStation ክፍል የተለያዩ ኃላፊነቶች (ወይም ተግባራት) አሉት። ሁሉም ወደ ዓይነቶች ሊከፋፈሉ ይችላሉ-

  • ዳሳሾች;
  • አቅርቦቶች (የፍጆታ ዕቃዎች);
  • ነዳጅ;
  • አፋጣኝ.

ከጣቢያው ሰራተኞች መካከል አንዳቸውም ክፍል ባይመደቡም ለምንድነው ተጠያቂው ማን እንደሆነ በቀላሉ መገመት እንችላለን። ምናልባትም ሳይንቲስቱ ሴንሰሮችን ይቆጣጠራሉ ፣ የሎጂስቲክስ ባለሙያው ሀብቶችን የማቅረብ ሃላፊነት አለባቸው ፣ መሐንዲሱ የነዳጅ አቅርቦቶች እና አብራሪው ማበረታቻዎችን ይቆጣጠራል።

ይህ ፕሮግራም SRP አያከብርም ማለት እንችላለን? አወ እርግጥ ነው. ነገር ግን የSpaceStation ክፍል ሁሉንም ነገር የሚያውቅ እና ሁሉን የሚያደርግ የተለመደ "የእግዚአብሔር ነገር" ነው። ይህ በነገር-ተኮር ፕሮግራሚንግ ውስጥ ዋና ፀረ-ንድፍ ነው። ለጀማሪዎች, እንደዚህ ያሉ ነገሮች ለመጠገን እጅግ በጣም አስቸጋሪ ናቸው. እስካሁን ድረስ ፕሮግራሙ በጣም ቀላል ነው, አዎ, ነገር ግን አዲስ ባህሪያትን ከጨመርን ምን እንደሚሆን አስብ. ምናልባት የእኛ የጠፈር ጣቢያ የሕክምና ጣቢያ ወይም የመሰብሰቢያ ክፍል ያስፈልገዋል. እና ብዙ ተግባራት ሲኖሩ፣ የበለጠ SpaceStation ያድጋል። ደህና ፣ ይህ ፋሲሊቲ ከሌሎች ጋር ስለሚገናኝ ፣ ሙሉውን ውስብስብ አገልግሎት ማገልገል የበለጠ ከባድ ይሆናል። በውጤቱም, የፍጥነት ማቀነባበሪያዎችን ሥራ ማደናቀፍ እንችላለን. አንድ ተመራማሪ በሴንሰሮች ላይ ለውጦችን ከጠየቀ፣ ይህ የጣቢያውን የግንኙነት ስርዓቶች በጥሩ ሁኔታ ሊጎዳ ይችላል።

የ SRP መርህን መጣስ ለአጭር ጊዜ ስልታዊ ድል ሊሰጥ ይችላል, ነገር ግን በመጨረሻ "ጦርነቱን እናጣለን", እና ለወደፊቱ እንዲህ ያለውን ጭራቅ ለመጠበቅ በጣም አስቸጋሪ ይሆናል. ፕሮግራሙን ወደ ተለያዩ የኮድ ክፍሎች መከፋፈል የተሻለ ነው, እያንዳንዱም አንድ የተወሰነ ክዋኔ የማከናወን ሃላፊነት አለበት. ይህንን በመረዳት የ SpaceStation ክፍልን እንለውጥ።

ኃላፊነትን እናከፋፍል

ከላይ በ SpaceStation ክፍል የሚቆጣጠሩትን አራት አይነት ኦፕሬሽኖችን ገለፅን። እንደገና በሚፈጥሩበት ጊዜ እናስታውሳቸዋለን. የተዘመነው ኮድ ከ SRP ጋር በተሻለ ሁኔታ ይዛመዳል።

class SpaceStation
  attr_reader :sensors, :supply_hold, :fuel_tank, :thrusters
 
  def initialize
    @supply_hold = SupplyHold.new
    @sensors = Sensors.new
    @fuel_tank = FuelTank.new
    @thrusters = Thrusters.new(@fuel_tank)
  end
end
 
class Sensors
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
end
 
class SupplyHold
  attr_accessor :supplies
 
  def initialize
    @supplies = {}
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
 
  def report_supplies
    puts "----- Supply Report -----"
    if @supplies.keys.length > 0
      @supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
end
 
class FuelTank
  attr_accessor :fuel
 
  def initialize
    @fuel = 0
  end
 
  def get_fuel_levels
    @fuel
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def use_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Using #{quantity} units of fuel from the tank."
    @fuel -= quantity
  end
 
  def report_fuel
    puts "----- Fuel Report -----"
    puts "#{@fuel} units of fuel available."
  end
end
 
class Thrusters
  def initialize(fuel_tank)
    @linked_fuel_tank = fuel_tank
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    if @linked_fuel_tank.get_fuel_levels >= 10
      puts "Thrusting action successful."
      @linked_fuel_tank.use_fuel(10)
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end

ብዙ ለውጦች አሉ, ፕሮግራሙ በእርግጠኝነት አሁን የተሻለ ይመስላል. አሁን የእኛ የSpaceStation ክፍል እንደ ሴንሰሮች ስብስብ፣ የፍጆታ አቅርቦት ሥርዓት፣ የነዳጅ ታንክ እና ማበልጸጊያዎችን ጨምሮ ለጥገኛ ክፍሎች ክዋኔዎች የሚጀመሩበት መያዣ ሆኗል።

ለማንኛውም ተለዋዋጮች አሁን ተጓዳኝ ክፍል አለ: ዳሳሾች; አቅርቦት መያዣ; የነዳጅ ታንክ; ግፈኞች።

በዚህ የኮዱ ስሪት ውስጥ በርካታ አስፈላጊ ለውጦች አሉ። ነጥቡ የግለሰቦች ተግባራት በራሳቸው ክፍሎች ውስጥ ብቻ የተቀመጡ አይደሉም, እነሱ ሊተነብዩ እና ወጥነት ባለው መልኩ እንዲደራጁ ይደረጋሉ. የመተሳሰሪያ መርህን ለመከተል ተመሳሳይ ተግባር ያላቸውን ንጥረ ነገሮች እንከፋፍላለን። አሁን፣ ከሃሽ መዋቅር ወደ አደራደር እየተሸጋገርን የስርአቱን አሰራር መቀየር ካስፈለገን የአቅርቦት ክፍልን ብቻ ተጠቀም፤ ሌሎች ሞጁሎችን መንካት የለብንም:: በዚህ መንገድ የሎጂስቲክስ ባለሥልጣኑ በእሱ ክፍል ውስጥ አንድ ነገር ቢቀይር, የተቀረው ጣቢያ ሳይበላሽ ይቆያል. በዚህ አጋጣሚ የSpaceStation ክፍል ለውጦቹን እንኳን አያውቅም።

በጠፈር ጣቢያው ላይ የሚሰሩ መኮንኖቻችን በለውጦቹ ደስተኛ ሊሆኑ ይችላሉ ምክንያቱም የሚፈልጉትን ሊጠይቁ ይችላሉ. ኮዱ በአቅራቢነት እና በነዳጅ ታንክ ክፍሎች ውስጥ እንደ ሪፖርት_አቅርቦት እና ሪፖርት_ነዳጅ ያሉ ዘዴዎች እንዳሉት ልብ ይበሉ። ምድር የምትዘግብበትን መንገድ እንድትቀይር ብትጠይቅ ምን ይሆናል? ሁለቱም ክፍሎች፣ SupplyHold እና FuelTank፣ መለወጥ አለባቸው። ነዳጅ እና የፍጆታ ዕቃዎች የሚቀርቡበትን መንገድ መቀየር ቢያስፈልግስ? ምናልባት ሁሉንም ተመሳሳይ ክፍሎችን እንደገና መቀየር ይኖርብዎታል. እና ይሄ ቀድሞውኑ የ SRP መርህ መጣስ ነው. ይህን እናስተካክል.

class SpaceStation
  attr_reader :sensors, :supply_hold, :supply_reporter,
              :fuel_tank, :fuel_reporter, :thrusters
 
  def initialize
    @sensors = Sensors.new
    @supply_hold = SupplyHold.new
    @supply_reporter = SupplyReporter.new(@supply_hold)
    @fuel_tank = FuelTank.new
    @fuel_reporter = FuelReporter.new(@fuel_tank)
    @thrusters = Thrusters.new(@fuel_tank)
  end
end
 
class Sensors
  def run_sensors
    puts "----- Sensor Action -----"
    puts "Running sensors!"
  end
end
 
class SupplyHold
  attr_accessor :supplies
  attr_reader :reporter
 
  def initialize
    @supplies = {}
  end
 
  def get_supplies
    @supplies
  end
 
  def load_supplies(type, quantity)
    puts "----- Supply Action -----"
    puts "Loading #{quantity} units of #{type} in the supply hold."
    
    if @supplies[type]
      @supplies[type] += quantity
    else
      @supplies[type] = quantity
    end
  end
 
  def use_supplies(type, quantity)
    puts "----- Supply Action -----"
    if @supplies[type] != nil && @supplies[type] > quantity
      puts "Using #{quantity} of #{type} from the supply hold."
      @supplies[type] -= quantity
    else
      puts "Supply Error: Insufficient #{type} in the supply hold."
    end
  end
end
 
class FuelTank
  attr_accessor :fuel
  attr_reader :reporter
 
  def initialize
    @fuel = 0
  end
 
  def get_fuel_levels
    @fuel
  end
 
  def load_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Loading #{quantity} units of fuel in the tank."
    @fuel += quantity
  end
 
  def use_fuel(quantity)
    puts "----- Fuel Action -----"
    puts "Using #{quantity} units of fuel from the tank."
    @fuel -= quantity
  end
end
 
class Thrusters
  FUEL_PER_THRUST = 10
 
  def initialize(fuel_tank)
    @linked_fuel_tank = fuel_tank
  end
 
  def activate_thrusters
    puts "----- Thruster Action -----"
    
    if @linked_fuel_tank.get_fuel_levels >= FUEL_PER_THRUST
      puts "Thrusting action successful."
      @linked_fuel_tank.use_fuel(FUEL_PER_THRUST)
    else
      puts "Thruster Error: Insufficient fuel available."
    end
  end
end
 
class Reporter
  def initialize(item, type)
    @linked_item = item
    @type = type
  end
 
  def report
    puts "----- #{@type.capitalize} Report -----"
  end
end
 
class FuelReporter < Reporter
  def initialize(item)
    super(item, "fuel")
  end
 
  def report
    super
    puts "#{@linked_item.get_fuel_levels} units of fuel available."
  end
end
 
class SupplyReporter < Reporter
  def initialize(item)
    super(item, "supply")
  end
 
  def report
    super
    if @linked_item.get_supplies.keys.length > 0
      @linked_item.get_supplies.each do |type, quantity|
        puts "#{type} avalilable: #{quantity} units"
      end
    else
      puts "Supply hold is empty."
    end
  end
end
 
iss = SpaceStation.new
 
iss.sensors.run_sensors
  # ----- Sensor Action -----
  # Running sensors!
 
iss.supply_hold.use_supplies("parts", 2)
  # ----- Supply Action -----
  # Supply Error: Insufficient parts in the supply hold.
iss.supply_hold.load_supplies("parts", 10)
  # ----- Supply Action -----
  # Loading 10 units of parts in the supply hold.
iss.supply_hold.use_supplies("parts", 2)
  # ----- Supply Action -----
  # Using 2 of parts from the supply hold.
iss.supply_reporter.report
  # ----- Supply Report -----
  # parts avalilable: 8 units
 
iss.thrusters.activate_thrusters
  # ----- Thruster Action -----
  # Thruster Error: Insufficient fuel available.
iss.fuel_tank.load_fuel(100)
  # ----- Fuel Action -----
  # Loading 100 units of fuel in the tank.
iss.thrusters.activate_thrusters
  # ----- Thruster Action -----
  # Thrusting action successful.
  # ----- Fuel Action -----
  # Using 10 units of fuel from the tank.
iss.fuel_reporter.report
  # ----- Fuel Report -----
# 90 units of fuel available.

በዚህ የቅርብ ጊዜ የፕሮግራሙ ስሪት፣ ኃላፊነቶቹ በሁለት አዳዲስ ክፍሎች ተከፍለዋል FuelReporter እና Supply Reporter። ሁለቱም የሪፖርተር ክፍል ልጆች ናቸው። በተጨማሪም፣ አስፈላጊ ከሆነ የሚፈለገው ንዑስ ክፍል እንዲጀመር በ SpaceStation ክፍል ላይ የአብነት ተለዋዋጮችን ጨምረናል። አሁን, ምድር ሌላ ነገር ለመለወጥ ከወሰነ, እኛ ለውጦችን በንዑስ ክፍሎች ላይ እናደርጋለን, እና ለዋናው ክፍል አይደለም.

እርግጥ ነው፣ አንዳንድ ክፍሎቻችን አሁንም እርስ በርስ ጥገኛ ናቸው። ስለዚህ የአቅርቦት ዘጋቢው ነገር በአቅራቢነት ላይ የተመሰረተ ሲሆን FuelReporter ደግሞ በ FuelTank ላይ ይወሰናል. እርግጥ ነው, ማጠናከሪያዎቹ ከነዳጅ ማጠራቀሚያ ጋር መያያዝ አለባቸው. ግን እዚህ ሁሉም ነገር ቀድሞውኑ አመክንዮአዊ ይመስላል, እና ለውጦችን ማድረግ በተለይ አስቸጋሪ አይሆንም - የአንድ ነገር ኮድ ማስተካከል ሌላውን በእጅጉ አይጎዳውም.

ስለዚህ, የእያንዳንዱ እቃዎች / ክፍሎች ኃላፊነቶች በትክክል የሚገለጹበት ሞዱል ኮድ ፈጠርን. ከእንደዚህ አይነት ኮድ ጋር መስራት ችግር አይደለም, እሱን ማቆየት ቀላል ስራ ይሆናል. ሙሉውን "መለኮታዊ ነገር" ወደ SRP ቀየርነው።

Skillbox ይመክራል፡

ምንጭ: hab.com

አስተያየት ያክሉ