Nulis kode fléksibel maké SOLID

Nulis kode fléksibel maké SOLID

Ti penerjemah: diterbitkeun pikeun anjeun artikel ku Severin Perez ngeunaan ngagunakeun prinsip SOLID dina programming. Inpormasi tina tulisan bakal mangpaat pikeun pamula sareng programer anu berpengalaman.

Upami anjeun nuju kana pamekaran, sigana anjeun kantos nguping prinsip SOLID. Éta ngamungkinkeun programer nyerat kode anu bersih, terstruktur sareng gampang dijaga. Eta sia noting yén dina programming aya sababaraha pendekatan kana kumaha carana leres ngalakukeun pakasaban husus. Spesialis anu béda-béda gaduh ide sareng pamahaman anu béda ngeunaan "jalan anu leres"; éta sadayana gumantung kana pangalaman unggal jalma. Nanging, ideu anu diproklamasikeun dina SOLID ditampi ku ampir sadaya wawakil komunitas IT. Aranjeunna janten titik awal pikeun mecenghulna sareng pamekaran seueur prakték manajemén pangembangan anu saé.

Hayu urang ngartos naon prinsip SOLID sareng kumaha aranjeunna ngabantosan urang.

Skillbox nyarankeun: Kursus praktis "Pamekar Mobile PRO".

Kami ngingetan: pikeun sakabéh pamiarsa "Habr" - diskon 10 rubles nalika enrolling dina sagala Tangtu Skillbox ngagunakeun "Habr" kode promosi.

Naon ari SOLID?

Istilah ieu mangrupa singketan, unggal hurup istilah mangrupa awal ngaran prinsip husus:

  • SPrinsip Tanggung Jawab. Hiji modul tiasa gaduh hiji sareng ngan hiji alesan pikeun robih.
  • nu Okalam / Prinsip katutup (prinsip kabuka / ditutup). Kelas jeung elemen séjén kudu kabuka pikeun extension, tapi ditutup keur modifikasi.
  •  nu LIskov Substitusi Prinsipna (Prinsip substitusi Liskov). Fungsi anu nganggo jinis dasar kedah tiasa nganggo subtipe tina jinis dasar tanpa terang.
  • nu IPrinsip Segregation Interface  (prinsip pamisahan panganteur). Éntitas software henteu kedah gumantung kana metode anu aranjeunna henteu dianggo.
  • nu Dependency Inversion Prinsip (prinsip inversi dependensi). Modul dina tingkat anu langkung luhur henteu kedah gumantung kana modul dina tingkat anu langkung handap.

Prinsip Tanggung Jawab Tunggal


Prinsip Tanggung Jawab Tunggal (SRP) nyatakeun yén unggal kelas atanapi modul dina program kedah nanggung jawab ngan hiji bagian tina fungsionalitas program éta. Salaku tambahan, unsur tanggung jawab ieu kedah ditugaskeun ka kelasna sorangan, tinimbang sumebar ka kelas anu teu aya hubunganana. pamekar SRP sarta lulugu evangelist, Robert S. Martin, ngajelaskeun akuntabilitas salaku alesan pikeun robah. Anjeunna mimitina ngajukeun istilah ieu salaku salah sahiji unsur karyana "Prinsip Desain Berorientasi Objek". Konsep ieu kalebet seueur pola konektipitas anu sateuacana ditetepkeun ku Tom DeMarco.

Konsep éta ogé ngawengku sababaraha konsép anu dirumuskeun ku David Parnas. Dua anu utama nyaéta enkapsulasi sareng nyumput inpormasi. Parnas pamadegan yén ngabagi sistem kana modul misah teu kudu dumasar kana analisis diagram blok atawa aliran palaksanaan. Salah sahiji modul kedah ngandung solusi khusus anu nyayogikeun inpormasi minimum ka klien.

Ku jalan kitu, Martin masihan conto anu pikaresepeun sareng manajer senior perusahaan (COO, CTO, CFO), anu masing-masing nganggo parangkat lunak bisnis khusus pikeun tujuan anu béda. Hasilna, salah sahiji aranjeunna tiasa nerapkeun parobahan dina parangkat lunak tanpa mangaruhan kapentingan manajer sanés.

Obyék ilahi

Sakumaha biasa, cara anu pangsaéna pikeun diajar SRP nyaéta ningali éta dina aksi. Hayu urang tingali bagian tina program anu henteu nuturkeun Prinsip Tanggung Jawab Tunggal. Ieu kode Ruby anu ngajelaskeun paripolah sareng atribut stasiun ruang angkasa.

Tinjau conto jeung coba nangtukeun ieu di handap:
Tanggung jawab obyék anu dinyatakeun dina kelas SpaceStation.
Jalma anu bisa jadi kabetot dina operasi stasiun spasi.

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

Sabenerna, stasiun ruang angkasa urang téh dysfunctional (kuring teu nyangka kuring bakal meunang telepon ti NASA kapan geura-giru), tapi aya hal pikeun nganalisis di dieu.

Ku kituna, kelas SpaceStation boga sababaraha tanggung jawab béda (atawa tugas). Sakabéh éta bisa dibagi kana jenis:

  • sénsor;
  • suplai (consumables);
  • suluh;
  • akselerator.

Sanaos teu aya karyawan stasiun anu ditugaskeun kelas, urang tiasa ngabayangkeun saha anu tanggung jawab. Paling dipikaresep, élmuwan ngadalikeun sensor, logistician tanggung jawab suplai sumberdaya, insinyur tanggung jawab suplai suluh, sarta pilot ngadalikeun boosters.

Naha urang tiasa nyarios yén program ieu henteu patuh SRP? Sumuhun, pasti. Tapi kelas SpaceStation mangrupikeun "obyek dewa" anu terang sadayana sareng ngalakukeun sadayana. Ieu mangrupikeun anti-pola utama dina pemrograman berorientasi obyék. Pikeun pemula, obyék sapertos kitu hésé pisan dijaga. Sajauh program éta saderhana pisan, enya, tapi bayangkeun naon anu bakal kajadian upami urang nambihan fitur énggal. Panginten stasiun ruang angkasa urang peryogi stasiun médis atanapi ruang rapat. Sareng langkung seueur fungsina, langkung seueur SpaceStation bakal tumbuh. Nya, sabab fasilitas ieu bakal dihubungkeun sareng anu sanés, ngalayanan sadayana kompleks bakal langkung sesah. Hasilna, urang tiasa ngaganggu operasi, contona, akselerator. Upami panalungtik nyuhunkeun parobihan kana sénsor, ieu tiasa mangaruhan pisan sistem komunikasi stasiun.

Ngalanggar prinsip SRP tiasa masihan kameunangan taktis jangka pondok, tapi tungtungna urang bakal "kalah perang", sareng éta bakal hésé pisan pikeun ngajaga monster sapertos kitu di hareup. Hadé pisan mun éta ngabagi program kana bagian misah kode, nu masing-masing tanggung jawab pikeun ngalakukeun operasi husus. Ngartos ieu, hayu urang robih kelas SpaceStation.

Hayu urang ngabagi tanggung jawab

Di luhur kami netepkeun opat jinis operasi anu dikawasa ku kelas SpaceStation. Urang bakal tetep dina pikiran nalika refactoring. Kodeu anu diropéa langkung saé cocog sareng 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

Aya seueur parobihan, programna pasti langkung saé ayeuna. Ayeuna kelas SpaceStation urang geus jadi leuwih ti hiji wadah nu operasi nu ngagagas pikeun bagian gumantung, kaasup sakumpulan sensor, sistem suplai consumable, tanki bahan beuleum, sarta boosters.

Pikeun salah sahiji variabel ayeuna aya kelas saluyu: Sensor; SupplyHold; Tangki Suluh; Tukang dorong.

Aya sababaraha parobahan penting dina versi kode ieu. Intina nyaéta yén fungsi individu henteu ngan ukur dibungkus dina kelasna sorangan, aranjeunna diatur ku cara anu tiasa diprediksi sareng konsisten. Urang ngagolongkeun elemen sareng fungsionalitas anu sami pikeun nuturkeun prinsip kohérénsi. Ayeuna, upami urang kedah ngarobih cara sistem jalanna, pindah tina struktur hash ka array, ngan nganggo kelas SupplyHold; urang henteu kedah nyabak modul anu sanés. Ku cara kieu, upami perwira logistik ngarobih hiji hal di bagian na, sesa stasion bakal tetep gembleng. Dina hal ieu, kelas SpaceStation malah moal sadar kana parobahanana.

Patugas kami anu damel di stasiun ruang angkasa sigana bagja ngeunaan parobihan sabab tiasa nyuhunkeun anu diperyogikeun. Perhatikeun yén kode ngabogaan métode kayaning report_supplies na report_fuel dikandung dina kelas SupplyHold na FuelTank. Naon anu bakal kajadian upami Bumi naroskeun ngarobih cara ngalaporkeun? Kadua kelas, SupplyHold sareng FuelTank, kedah dirobih. Kumaha upami anjeun kedah ngarobih cara pangiriman bahan bakar sareng bahan bakar? Anjeun panginten kedah ngarobih deui kelas anu sami. Sareng ieu parantos ngalanggar prinsip SRP. Hayu urang ngalereskeun ieu.

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.

Dina versi panganyarna tina program ieu, tanggung jawab geus dibagi jadi dua kelas anyar, FuelReporter na SupplyReporter. Duanana barudak kelas Reporter. Salaku tambahan, kami nambihan variabel conto kana kelas SpaceStation supados subclass anu dipikahoyong tiasa diinisialisasi upami diperyogikeun. Ayeuna, upami Bumi mutuskeun pikeun ngarobih hal anu sanés, maka urang bakal ngarobih kana subclass, sanés ka kelas utama.

Tangtosna, sababaraha kelas urang masih silih gumantung. Ku kituna, objék SupplyReporter gumantung kana SupplyHold, sarta FuelReporter gumantung kana FuelTank. Tangtu, boosters kudu disambungkeun ka tanki bahan beuleum. Tapi di dieu sagalana geus Sigana logis, sarta nyieun parobahan moal utamana hésé - ngédit kodeu hiji objek moal greatly mangaruhan sejen.

Ku kituna, kami geus dijieun kode modular dimana tanggung jawab unggal objék / kelas anu persis ditetepkeun. Gawe sareng kode sapertos henteu masalah, ngajaga eta bakal tugas basajan. Kami parantos ngarobih sadayana "obyek ketuhanan" kana SRP.

Skillbox nyarankeun:

sumber: www.habr.com

Tambahkeun komentar