Comment écrire du code qui pense Ruby orienté objet

Qu'est-ce que l'orientation objet?

C'est une méthode de conception, pas une grammaire. Quand je l'ai entendu pour la première fois, je pensais que c'était une sorte de grammaire ou de syntaxe, mais ce n'est pas le cas. Il semble que la compréhension correcte est que Ruby a une syntaxe facile à écrire en fonction de l'orientation de l'objet.

Pourquoi écrire orienté objet

Je pense qu'il y a quelque chose dedans. Cette fois, je vais me concentrer sur ce point et le résumer.

Tout d'abord, essayez d'écrire sans penser à l'orientation de l'objet

toriaezuugoku.rb


x_max = ARGV[0]
y_max = ARGV[1]

if !x_max || !y_max
  puts "Veuillez spécifier l'argument"
  exit 1
end

x_max =  x_max.to_i
y_max =  y_max.to_i

#Statut
x = 1
y = 1
step = 1
x_way = 1
y_way = 1

puts " #{step} (#{x},#{y})"
x += 1
y += 1
loop do
  step += 1
  puts " #{step} (#{x},#{y})"

  if y == 1 && x == x_max || x == 1 && y == y_max || x == 1 && y == 1 || x == x_max && y == y_max
    puts "GOAL!!"
    break
  elsif x == x_max
    x_way = -1
  elsif y == y_max
    y_way = -1
  elsif x == 1
    x_way = 1
  elsif y == 1
    y_way = 1
  end

  x += x_way
  y += y_way
end

C'est le code que j'ai écrit dans l'article précédent. Il est écrit de manière à ce que tous les traitements se déroulent de haut en bas sans définir de fonction. C'est le code le plus difficile à lire. En effet, les spécifications sont telles que la fonction ne peut être lue que si le traitement réel est suivi par la prédiction de ce qui est fait à partir de la définition de la variable. Abandonnons d'essayer de lire ce que ce code fait au fil du temps ici et regardons le code réécrit ensuite.

methodtohash.rb


def move_ball(hash)
	hash["x"] += hash["x_way"]
	hash["y"] += hash["y_way"]
	hash["step"] += 1
end

def reflect_x(hash)
	if hash["x_way"] == 1
		hash["x_way"] = -1
  elsif hash["x_way"] == -1
    hash["x_way"] = 1
  end
end

def reflect_y(hash)
	if hash["y_way"] == 1
		hash["y_way"] = -1
  elsif hash["y_way"] == -1
    hash["y_way"] = 1
  end
end

def goal?(hash, x_max, y_max)
  hash["y"] == 1 && hash["x"] == x_max \
  || hash["x"] == 1 && hash["y"] == y_max \
  || hash["x"] == 1 && hash["y"] == 1 \
  || hash["x"] == x_max && hash["y"] == y_max
end

def boundary_x?(hash, x_max)
  hash["x"] == x_max || hash["x"] == 1
end

def boundary_y?(hash, y_max)
  hash["y"] == y_max || hash["y"] == 1
end

x_max = ARGV[0]
y_max = ARGV[1]

if !x_max || !y_max
  puts "Veuillez spécifier l'argument"
  exit 1
end

x_max =  x_max.to_i
y_max =  y_max.to_i

state = {
  "x" => 1,
  "y" => 1,
  "step" => 1,
  "x_way" => 1,
  "y_way" => 1
}

puts " #{state["step"]} (#{state["x"]},#{state["y"]})"

loop do
  move_ball(state)

  puts " #{state["step"]} (#{state["x"]},#{state["y"]})"

  if goal?(state, x_max, y_max)
    puts "GOAL!!"
    break
  elsif boundary_x?(state, x_max)
    reflect_x(state)
  elsif boundary_y?(state, y_max)
    reflect_y(state)
  end
end

Cette fois, je l'ai écrit en utilisant la définition de la fonction et le hachage. En regardant l'instruction de boucle, il semble que l'état du contenu de l'état appelé par hachage est manipulé par la méthode appelée move_ball, et x et y lorsque la balle rebondit avec boundary_x et boundary_y dans l'expression conditionnelle. Je me demande si les coordonnées sont changées par reflect_x et reflect_y ... Cela devient quelque chose qui ne peut pas être lu. Si le traitement du contenu défini par la fonction est correct, le lecteur pourra facilement comprendre ce qu'il fait et ce que fait ce code en ne lisant qu'ici. Il y a un tel mérite si vous divisez la partie du moment où l'état est changé en fonctions. En outre, l'utilisation de hachages rend la gestion des états plus sûre que dans le code précédent, qui était représenté à l'aide de nombreuses variables globales, et est toujours un peu plus flexible à mesure que le nombre de balles augmente. (Si le nombre de balles est déraisonnablement grand ou inconnu, nous ne pouvons pas le gérer) Cependant, cela ne suffit toujours pas à la recherche de la lisibilité. De plus, si vous faites une erreur telle qu'un hachage, ce code ne retournera que nul, ce qui rend plus difficile la détection de l'erreur. Plus le produit est gros, plus il est difficile à découvrir.

C'est donc un virage orienté objet.

object.rb


class Ball
	attr_accessor :x, :y, :x_way, :y_way, :step

	def initialize(x: 1, y: 1, x_way: 1, y_way: 1, step: 1)
		@x = x
		@y = y
		@x_way = x_way
		@y_way = y_way
		@step = step
	end
	
	def move
		@x += @x_way
		@y += @y_way
		@step += 1
	end

	def reflect_x
		if @x_way == 1
			@x_way = -1
		elsif @x_way == -1
			@x_way = 1
		end
	end

	def reflect_y
		if @y_way == 1
			@y_way = -1
		elsif @y_way == -1
			@y_way = 1
		end
	end

	def goal?(x_max, y_max)
		@x == x_max && y == 1 \
		|| @x == 1 && @y == y_max \
		|| @x == 1 && @y == 1 \
		|| @x == x_max && @y == y_max
	end

	def boundary_x?(x_max)
		@x == x_max || @x == 1
	end

	def boundary_y?(y_max)
		@y == y_max || @y == 1
	end
end

class BilliardTable
	attr_accessor :length_x, :length_y, :ball

	def initialize(length_x: nil, length_y: nil, ball: nil)
		@length_x = length_x
		@length_y = length_y
		@ball = ball
	end

	def cue
		print_status

		loop do
			@ball.move

			print_status
		
			if @ball.goal?(@length_x, @length_y)
				puts "GOAL!!"
				break
			elsif @ball.boundary_x?(@length_x)
				@ball.reflect_x
			elsif @ball.boundary_y?(@length_y)
				@ball.reflect_y
			end
		end
	end

	def print_status
		puts "#{@ball.step}, (#{@ball.x}, #{@ball.y})"
	end
end

x_max = ARGV[0]
y_max = ARGV[1]

if !x_max || !y_max
	puts "Veuillez spécifier l'argument"
	exit 1
end

x_max =  x_max.to_i
y_max =  y_max.to_i

ball = Ball.new()

bt = BilliardTable.new(length_x: x_max, length_y: y_max, ball: ball)

bt.cue

Vous pourrez écrire comme ci-dessus. L'état de la balle et l'état de la table de billard sont divisés en classes, et la méthode d'opération d'état requise pour la fonction de la balle est définie dans la classe de balle, et le traitement de l'opération sur la table sur laquelle la balle est déplacée est défini dans la classe de billard. Donc, en fait, si vous voulez connaître le comportement général de ce code, vous pouvez le saisir en lisant le système de traitement de la classe billiard et la partie qui reçoit l'argument de ligne de commande, c'est-à-dire la partie de code écrite en dehors de la classe. Je peux. La convention de dénomination anglaise est également beaucoup plus facile à lire. Enfin, lorsque vous exécutez cue (poke) contre bt (billard stand), la balle bouge. Très facile à lire. De plus, comme les opérations d'état sont définies avec beaucoup de détails, je pense que la facilité de modification du code et la facilité d'appeler la méthode se sont également améliorées. Je pense que cela rend la résolution de bogues plus facile que les deux codes solides et le code de hachage que j'ai mentionnés plus tôt.

Résumé

J'ai essayé de réécrire le code en deux étapes du processus d'écriture solide que j'ai écrit en premier, mais j'ai compris à quel point il est difficile de lire le code que j'ai écrit en premier, pour ainsi dire, c'est du code sale. .. J'écrivais beaucoup de traitement de code comme le premier que j'ai écrit dans rails, qui est un cadre conçu pour faciliter la conception de classes par orientation objet, donc je créais du code yabe. Était encore plus important et bien compris.

Recommended Posts

Comment écrire du code qui pense Ruby orienté objet
Comment écrire du bon code
Comment rédiger un code facile à comprendre [Résumé 3]
Écrire du code difficile à tester
Comment écrire des rails
Écrire du code facile à maintenir (partie 1)
Comment écrire docker-compose
Comment écrire Mockito
Écrire du code facile à maintenir (partie 4)
Comment écrire du code de test avec la certification de base
Comment écrire un fichier de migration
Écrire du code facile à maintenir (partie 3)
[Ruby on Rails] Comment écrire enum en japonais
Comment utiliser le retour Ruby
Comment rédiger un commentaire java
Ruby: Comment utiliser les cookies
Comment écrire Junit 5 organisé
Comment écrire des graines de Rails
Comment écrire le routage Rails
Comparaison de l'écriture des fonctions de rappel (Java, JavaScript, Ruby)
Nouvelles fonctionnalités de Java 14 pouvant être utilisées pour écrire du code
Comment itérer indéfiniment en Ruby
Étudier Java # 6 (Comment écrire des blocs)
Comment installer Ruby via rbenv
Comment installer Bootstrap dans Ruby
[Rails] Comment écrire la gestion des exceptions?
Comment écrire une déclaration de variable Java
Comment utiliser la méthode Ruby inject
Comment exécuter l'IRB de Ruby (Ruby interactif)
J'ai essayé d'écrire du code comme une déclaration de type en Ruby
[R Spec on Rails] Comment écrire du code de test pour les débutants par les débutants
Comment écrire un exemple d'implémentation F03 ruby et C en temps réel hors ligne
Comment écrire Ruby en une seule ligne Résumé par débutant
Écrivons un code facile à maintenir (Partie 2) Nom
[Basique] Comment écrire un auto-apprentissage Dockerfile ②
[Introduction à Java] Comment écrire un programme Java
Comment appeler le code Swift 5.3 depuis Objective-C
Longueur, taille, nombre de rubis Comment utiliser
[Java] Comment sortir et écrire des fichiers!
[Ruby] Comment utiliser slice pour les débutants
[Ruby on Rails] Comment utiliser redirect_to
[Facile] Comment mettre à niveau Ruby et le bundler
Écrire du code à l'aide de classes et d'instances Ruby
Comment écrire un spécificateur de coupe de point Spring AOP
Ruby: CSV :: Comment utiliser la note de tableau