Share This
Связаться со мной
Крути в низ
Categories
//Введение в ООП: класс Blob и модульность

Введение в ООП: класс Blob и модульность

19.09.2020Category : Python

Предыдущая статья — Введение в объектно-ориентированное программирование: много клякс.

Добро пожаловать в следующую часть нашей серии статей про объектно-ориентированное программирование. В этой статье мы обсудим модульность написанного нами класса.

Одними из основных признаков хорошего кода являются поддерживаемость, масштабируемость и модульность.

Пока мы не представили ничего, что с течением времени сделало бы наш код слишком сложным для поддержки или масштабирования, по крайней мере, в рамках того, что мы можем делать с библиотекой PyGame. А как насчет того, чтобы сделать его модульным? Для этого есть действительно простой тест, давайте попробуем его импортировать!

Для этого мы разобьем наш код на два файла. Давайте скопируем класс Blob и импорт библиотеки random в новый файл blob.py:

import random   class Blob:      def __init__(self, color):         self.x = random.randrange(0, WIDTH)         self.y = random.randrange(0, HEIGHT)         self.size = random.randrange(4,8)         self.color = color      def move(self):         self.move_x = random.randrange(-1,2)         self.move_y = random.randrange(-1,2)         self.x += self.move_x         self.y += self.move_y          if self.x < 0: self.x = 0         elif self.x > WIDTH: self.x = WIDTH                  if self.y < 0: self.y = 0         elif self.y > HEIGHT: self.y = HEIGHT 

В первоначальном файле удалим класс Blob, а затем импортируем его из модуля blob.py:

import pygame import random from blob import Blob  STARTING_BLUE_BLOBS = 10 STARTING_RED_BLOBS = 3  WIDTH = 800 HEIGHT = 600 WHITE = (255, 255, 255) BLUE = (0, 0, 255) RED = (255, 0, 0)  game_display = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Blob World") clock = pygame.time.Clock()  def draw_environment(blob_list):     game_display.fill(WHITE)      for blob_dict in blob_list:         for blob_id in blob_dict:             blob = blob_dict[blob_id]             pygame.draw.circle(game_display, blob.color, [blob.x, blob.y], blob.size)             blob.move()      pygame.display.update()       def main():     blue_blobs = dict(enumerate([Blob(BLUE) for i in range(STARTING_BLUE_BLOBS)]))     red_blobs = dict(enumerate([Blob(RED) for i in range(STARTING_RED_BLOBS)]))     while True:         for event in pygame.event.get():             if event.type == pygame.QUIT:                 pygame.quit()                 quit()         draw_environment([blue_blobs,red_blobs])         clock.tick(60)  if __name__ == '__main__':     main() 

Сразу же мы получаем ошибку в модуле blob.py относительно нашего класса Blob, где у нас появилось несколько неопределенных переменных. Это явно проблема с написанием классов: вам следует избегать использования констант или переменных вне класса. Давайте добавим эти значения в метод __init__, а затем изменим все части, в которых мы использовали эти константы.

Итак, обновим файл blob.py следующим образом:

import random   class Blob:      def __init__(self, color, x_boundary, y_boundary):         self.x_boundary = x_boundary         self.y_boundary = y_boundary         self.x = random.randrange(0, self.x_boundary)         self.y = random.randrange(0, self.y_boundary)         self.size = random.randrange(4,8)         self.color = color               def move(self):         self.move_x = random.randrange(-1,2)         self.move_y = random.randrange(-1,2)         self.x += self.move_x         self.y += self.move_y          if self.x < 0: self.x = 0         elif self.x > self.x_boundary: self.x = self.x_boundary                  if self.y < 0: self.y = 0         elif self.y > self.y_boundary: self.y = self.y_boundary 

Теперь в нашем исходном файле класс Blob при вызове ожидает определенных значений для этих аргументов, поэтому их нужно добавить в главную функцию:

def main():     blue_blobs = dict(enumerate([Blob(BLUE,WIDTH,HEIGHT) for i in range(STARTING_BLUE_BLOBS)]))     red_blobs = dict(enumerate([Blob(RED,WIDTH,HEIGHT) for i in range(STARTING_RED_BLOBS)]))     while True:         ...

Отлично, теперь наш класс Blob можно как минимум импортировать, так что он уже является модульным по своей сути! Есть еще одна хорошая идея: попробовать дать как можно больше возможностей разработчику, использующему ваш код, а также сделать ваш класс максимально универсальным.

Есть по крайней мере один момент, в котором мы определенно могли бы дать несколько больше свободы программисту, использующему этот класс. Это определение размера нашей кляксы:

 self.size = random.randrange(4,8)

Есть ли хоть одна причина, по которой не стоит давать программисту простой способ это менять? Мы таких причин не видим. Однако, в отличие от значений x_boundary и y_boundary, нам не обязательно нужен программист, который даст нам значение размера, поскольку мы можем, по крайней мере, использовать разумное начальное значение по умолчанию. То есть мы можем сделать так:

class Blob:      def __init__(self, color, x_boundary, y_boundary, size_range=(4,8)):         self.x_boundary = x_boundary         self.y_boundary = y_boundary         self.x = random.randrange(0, self.x_boundary)         self.y = random.randrange(0, self.y_boundary)         self.size = random.randrange(size_range[0],size_range[1])         self.color = color

Теперь, если программист захочет изменить размер, он сможет это сделать (но может и не делать). Мы также разрешили программисту изменять скорость кляксы, если есть такое желание:

import random   class Blob:      def __init__(self, color, x_boundary, y_boundary, size_range=(4,8), movement_range=(-1,2)):         self.size = random.randrange(size_range[0],size_range[1])         self.color = color         self.x_boundary = x_boundary         self.y_boundary = y_boundary         self.x = random.randrange(0, self.x_boundary)         self.y = random.randrange(0, self.y_boundary)         self.movement_range = movement_range      def move(self):         self.move_x = random.randrange(self.movement_range[0],self.movement_range[1])         self.move_y = random.randrange(self.movement_range[0],self.movement_range[1])         self.x += self.move_x         self.y += self.move_y          if self.x < 0: self.x = 0         elif self.x > self.x_boundary: self.x = self.x_boundary                  if self.y < 0: self.y = 0         elif self.y > self.y_boundary: self.y = self.y_boundary 

Итак, мы немного разобрали наш класс Blob. Что-нибудь еще бросается в глаза? Нам кажется, что еще надо подумать о границах, в которых наша клякса может существовать.

Могут ли быть примеры, в которых мы бы хотели, чтобы кляксы могли свободно перемещаться вне поля зрения? Безусловно! Но полезен ли этот код для создания границ? Возможно ли, что программисты захотят использовать его довольно часто? Безусловно!

В общем, имеет смысл либо совсем не иметь такого кода, либо выделить его в отдельный собственный метод, например:

import random  class Blob:      def __init__(self, color, x_boundary, y_boundary, size_range=(4,8), movement_range=(-1,2)):         self.size = random.randrange(size_range[0],size_range[1])         self.color = color         self.x_boundary = x_boundary         self.y_boundary = y_boundary         self.x = random.randrange(0, self.x_boundary)         self.y = random.randrange(0, self.y_boundary)         self.movement_range = movement_range      def move(self):         self.move_x = random.randrange(self.movement_range[0],self.movement_range[1])         self.move_y = random.randrange(self.movement_range[0],self.movement_range[1])         self.x += self.move_x         self.y += self.move_y      def check_bounds(self):         if self.x < 0: self.x = 0         elif self.x > self.x_boundary: self.x = self.x_boundary                  if self.y < 0: self.y = 0         elif self.y > self.y_boundary: self.y = self.y_boundary 

Теперь программист может самостоятельно решить, использовать ему эти границы или нет. Вы также можете добавить некий новый аргумент в метод move. И если этот аргумент будет принимать значение True, то тогда границы будут применены.

В следующей статье серии, посвященной объектно-ориентированному программированию, мы обсудим наследование.

Следующая статья — Введение в объектно-ориентированное программирование: наследование.

    Проходите тест по Python и поймите, готовы ли вы идти на курсы
  • 17 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close