Gisulat namo ang flexible code gamit ang SOLID

Gisulat namo ang flexible code gamit ang SOLID

Gikan sa tighubad: gipatik para nimo artikulo ni Severin Perez mahitungod sa paggamit sa SOLID nga mga prinsipyo sa programming. Ang impormasyon gikan sa artikulo mahimong mapuslanon sa mga nagsugod ug eksperyensiyadong mga programmer.

Kung naa ka sa pag-uswag, lagmit nakadungog ka bahin sa SOLID nga mga prinsipyo. Gitugotan nila ang programmer sa pagsulat nga limpyo, maayo ang pagkahan-ay ug dali nga mapadayon nga code. Angay nga matikdan nga sa pagprograma adunay daghang mga pamaagi kung giunsa ang husto nga paghimo sa usa ka partikular nga trabaho. Ang lain-laing mga espesyalista adunay lain-laing mga ideya ug pagsabot sa "matarong nga dalan"; kini tanan nagdepende sa kasinatian sa matag tawo. Bisan pa, ang mga ideya nga giproklamar sa SOLID gidawat sa hapit tanan nga mga representante sa komunidad sa IT. Nahimo silang sinugdanan nga punto alang sa pagtungha ug pag-uswag sa daghang maayong pamatasan sa pagdumala sa kalamboan.

Atong sabton kung unsa ang SOLID nga mga prinsipyo ug giunsa kini pagtabang kanato.

Girekomenda sa Skillbox: Praktikal nga kurso "Mobile Developer PRO".

Gipahinumduman namon ikaw: alang sa tanan nga mga magbabasa sa "Habr" - usa ka diskwento sa 10 nga mga rubles kung nagpalista sa bisan unsang kurso sa Skillbox gamit ang code sa promosyon nga "Habr".

Unsa ang SOLID?

Kini nga termino usa ka abbreviation, ang matag letra sa termino mao ang sinugdanan sa ngalan sa usa ka piho nga prinsipyo:

  • SPrinsipyo sa Responsibilidad. Ang usa ka module mahimong adunay usa ug usa ra ka hinungdan sa pagbag-o.
  • ang Open/Sirado nga Prinsipyo (bukas/sirado nga prinsipyo). Ang mga klase ug uban pang mga elemento kinahanglan nga bukas alang sa extension, apan sirado alang sa pagbag-o.
  • β€Šang LIskov Pagpuli nga Prinsipyo (Liskov substitution nga prinsipyo). Ang mga gimbuhaton nga naggamit sa usa ka base nga tipo kinahanglan nga makagamit sa mga subtype sa base nga tipo nga wala nahibal-an kini.
  • ang IPrinsipyo sa Pagbulag sa Interfaceβ€Š (prinsipyo sa pagbulag sa interface). Ang mga entidad sa software kinahanglan dili magdepende sa mga pamaagi nga wala nila gigamit.
  • ang Dependency Inversion nga Prinsipyo (prinsipyo sa pagsalig sa pagsalig). Ang mga module sa mas taas nga lebel kinahanglan dili magdepende sa mga module sa ubos nga lebel.

Usa ka Prinsipyo sa Responsibilidad

β€Š
Ang Single Responsibility Principle (SRP) nag-ingon nga ang matag klase o module sa usa ka programa kinahanglan nga responsable sa usa lamang ka bahin sa pagpaandar sa programa. Dugang pa, ang mga elemento niini nga responsibilidad kinahanglan nga itudlo sa ilang kaugalingon nga klase, imbes nga magkatag sa wala’y kalabutan nga mga klase. Ang developer ug chief evangelist sa SRP, si Robert S. Martin, naghulagway sa pagkamay-tulubagon isip rason sa kausaban. Siya orihinal nga nagsugyot niini nga termino isip usa sa mga elemento sa iyang trabaho nga "Principles of Object-Oriented Design". Ang konsepto naglakip sa kadaghanan sa sumbanan sa koneksyon nga kaniadto gihubit ni Tom DeMarco.

Ang konsepto naglakip usab sa daghang mga konsepto nga gimugna ni David Parnas. Ang duha nga nag-una mao ang encapsulation ug pagtago sa impormasyon. Nangatarungan si Parnas nga ang pagbahin sa usa ka sistema sa lainlaing mga module dili kinahanglan ibase sa pagtuki sa mga block diagram o mga agos sa pagpatuman. Ang bisan unsang mga module kinahanglan adunay usa ka piho nga solusyon nga naghatag labing gamay nga kasayuran sa mga kliyente.

Pinaagi sa dalan, si Martin naghatag usa ka makapaikag nga pananglitan sa mga senior managers sa usa ka kompanya (COO, CTO, CFO), nga ang matag usa naggamit sa piho nga software sa negosyo alang sa lainlaing mga katuyoan. Ingon usa ka sangputanan, bisan kinsa sa kanila ang makapatuman sa mga pagbag-o sa software nga dili makaapekto sa interes sa ubang mga managers.

Balaan nga butang

Sama sa naandan, ang labing maayong paagi sa pagkat-on sa SRP mao ang pagtan-aw niini sa aksyon. Atong tan-awon ang usa ka seksyon sa programa nga DILI nagsunod sa Single Responsibility Principle. Kini ang Ruby code nga naghulagway sa kinaiya ug mga kinaiya sa estasyon sa kawanangan.

Ribyuha ang pananglitan ug sulayi pagtino ang mosunod:
Mga responsibilidad sa mga butang nga gipahayag sa klase sa SpaceStation.
Kadtong mahimong interesado sa operasyon sa estasyon sa kawanangan.

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

Sa tinuud, ang among estasyon sa kawanangan dili magamit (sa akong hunahuna dili ako makadawat usa ka tawag gikan sa NASA bisan unsang orasa), apan adunay usa ka butang nga analisahon dinhi.

Sa ingon, ang klase sa SpaceStation adunay daghang lainlaing mga responsibilidad (o mga buluhaton). Ang tanan niini mahimong bahinon sa mga tipo:

  • mga sensor;
  • mga suplay (mga gamit);
  • sugnod;
  • mga accelerator.

Bisan og walay usa sa mga empleyado sa estasyon ang gi-assign og klase, dali ra natong mahunahuna kung kinsa ang responsable sa unsa. Lagmit, ang siyentista nagkontrol sa mga sensor, ang logistician ang responsable sa pagsuplay sa mga kahinguhaan, ang inhenyero ang responsable sa mga suplay sa gasolina, ug ang piloto ang nagkontrol sa mga booster.

Makaingon ba ta nga kini nga programa dili uyon sa SRP? Oo, sigurado. Apan ang klase sa SpaceStation usa ka tipikal nga "dios nga butang" nga nahibal-an ang tanan ug gibuhat ang tanan. Kini usa ka mayor nga anti-pattern sa object-oriented programming. Alang sa usa ka nagsugod, ang ingon nga mga butang lisud kaayo nga mapadayon. Sa pagkakaron ang programa yano ra kaayo, oo, apan hunahunaa kung unsa ang mahitabo kung magdugang kami mga bag-ong bahin. Tingali ang atong estasyon sa kawanangan magkinahanglan ug medikal nga estasyon o lawak tigomanan. Ug kung daghan ang mga gimbuhaton, labi nga motubo ang SpaceStation. Aw, tungod kay kini nga pasilidad makonektar sa uban, ang pag-alagad sa tibuok complex mahimong mas komplikado. Ingon usa ka sangputanan, mahimo naton mabalda ang operasyon sa, pananglitan, mga accelerator. Kung ang usa ka tigdukiduki mangayo mga pagbag-o sa mga sensor, mahimo’g makaapekto kini sa mga sistema sa komunikasyon sa istasyon.

Ang paglapas sa prinsipyo sa SRP mahimong maghatag sa usa ka hamubo nga taktikal nga kadaugan, apan sa katapusan kita "mapildi sa gubat", ug mahimong lisud kaayo ang pagpadayon sa ingon nga mangtas sa umaabot. Labing maayo nga bahinon ang programa sa lainlaing mga seksyon sa code, nga ang matag usa responsable sa paghimo sa usa ka piho nga operasyon. Sa pagsabut niini, usbon nato ang klase sa SpaceStation.

Iapod-apod nato ang responsibilidad

Sa ibabaw gihubit namo ang upat ka matang sa mga operasyon nga kontrolado sa klase sa SpaceStation. Atong hinumdoman sila sa dihang mag-refactor. Ang gi-update nga code mas mohaum sa 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

Adunay daghang mga pagbag-o, ang programa siguradong mas maayo karon. Karon ang among SpaceStation nga klase nahimong labaw pa sa usa ka sudlanan diin ang mga operasyon gisugdan alang sa nagsalig nga mga bahin, lakip ang usa ka set sa mga sensor, usa ka sistema sa suplay nga magamit, usa ka tangke sa gasolina, ug mga booster.

Alang sa bisan unsang mga baryable aduna nay katugbang nga klase: Mga Sensor; SupplyHold; FuelTank; Mga thruster.

Adunay ubay-ubay nga importanteng kausaban niini nga bersyon sa code. Ang punto mao nga ang indibidwal nga mga gimbuhaton dili lamang gilakip sa ilang kaugalingon nga mga klase, kini giorganisar sa paagi nga mahimong matag-an ug makanunayon. Gigrupo namon ang mga elemento nga adunay parehas nga pagpaandar aron sundon ang prinsipyo sa pagkadugtong. Karon, kung kinahanglan naton usbon ang paagi sa paglihok sa sistema, pagbalhin gikan sa usa ka istruktura sa hash hangtod sa usa ka array, gamita lang ang klase sa SupplyHold; dili na kinahanglan nga hikap ang ubang mga module. Niining paagiha, kung ang opisyal sa logistik magbag-o sa usa ka butang sa iyang seksyon, ang nahabilin nga estasyon magpabilin nga wala. Sa kini nga kaso, ang klase sa SpaceStation dili gani makahibalo sa mga pagbag-o.

Ang among mga opisyal nga nagtrabaho sa estasyon sa kawanangan tingali nalipay sa mga pagbag-o tungod kay makahangyo sila sa ilang gikinahanglan. Matikdi nga ang code adunay mga pamaagi sama sa report_supplies ug report_fuel nga anaa sa SupplyHold ug FuelTank nga mga klase. Unsa ang mahitabo kung ang Yuta mohangyo nga usbon ang paagi sa pagreport niini? Ang duha ka klase, SupplyHold ug FuelTank, kinahanglang usbon. Unsa kaha kung kinahanglan nimo nga usbon ang paagi sa paghatud sa gasolina ug mga gamit? Tingali kinahanglan nimo nga usbon ang tanan nga parehas nga mga klase pag-usab. Ug kini usa na ka paglapas sa prinsipyo sa SRP. Ayuhon nato ni.

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.

Niining pinakaulahing bersyon sa programa, ang mga responsibilidad gibahin sa duha ka bag-ong klase, FuelReporter ug SupplyReporter. Parehas silang anak sa klase sa Reporter. Dugang pa, gidugang namo ang mga variable nga pananglitan sa klase sa SpaceStation aron ang gusto nga subclass mahimong masugdan kung gikinahanglan. Karon, kung ang Yuta nakahukom sa pag-usab sa lain nga butang, nan maghimo kami mga pagbag-o sa mga subclass, ug dili sa panguna nga klase.

Siyempre, ang uban sa among mga klase nagdepende gihapon sa usag usa. Busa, ang butang nga SupplyReporter nagdepende sa SupplyHold, ug ang FuelReporter nagdepende sa FuelTank. Siyempre, ang mga booster kinahanglan nga konektado sa tangke sa gasolina. Apan dinhi ang tanan tan-awon nga makatarunganon, ug ang paghimo sa mga pagbag-o dili labi ka lisud - ang pag-edit sa code sa usa ka butang dili makaapekto sa lain.

Busa, naghimo kami ug modular code diin ang mga responsibilidad sa matag usa sa mga butang/klase tukma nga gihubit. Ang pagtrabaho sa ingon nga code dili usa ka problema, ang pagpadayon niini usa ka yano nga buluhaton. Atong gikabig ang tibuok β€œbalaang butang” ngadto sa SRP.

Girekomenda sa Skillbox:

Source: www.habr.com

Idugang sa usa ka comment