1 Востаннє редагувалося ping (06.05.2019 11:39:05)

Тема: Правильний ООП підхід для рішення задачі

Привіт.
Отже, дано два класи:
Group , екземпляром якої є група студентів. груп може бути кілька
група містить студентів, які до неї належать і має instance метод додати студента

Student - екземпляр студент, має поле - name,  може належати тільки до однієї групи або не належати до жодної.

питання - коли реалізовуємо метод Group().add_student(Student('Jan'))
де повинна зберігатися інформація про належність студента до якоїсь групи?
Зберігати на рівні Group список всіх груп і методом класу Group перевіряти - чи студент не входить в якусь з них?

class Group():
    groups = set()
    
    @classmethod
    def which_group(cls, student):
        res = []
        for group in cls.groups:
            if student in group.members:
                res.append(group)                
        return res
    
    def __init__(self, name):
        self.name = name
        self.members = set()
        Group.groups.add(self)
        
    def add_student(self, student):
        if not Group.which_group(student):
            self.members.add(student)
        else:
            print(student, 'already in some group')
        
    
        

class Student():
    
    def __init__(self, name):
        self.name = name

    def enroll_to_group(self, group):
        group.add_student(self)

наскільке таке рішення ідеологічно вірне?

2 Востаннє редагувалося koala (06.05.2019 13:15:44)

Re: Правильний ООП підхід для рішення задачі

Загалом це залежить від того, що саме ви збираєтеся робити далі з цими студентами та групами.
Тобто базових рішень два: або студент пам'ятає, з якої він групи, або група пам'ятає, які у ній студенти. Третій варіант - вони це пам'ятають одночасно, а цілісність забезпечується інкапсуляцією: функції, що додають/прибирають студентів до/з груп, відстежують, щоб і там, і там інформація була коректною. Якщо вам треба буде лише виводити на сторінці студента інформацію про групу - то у студента має бути посилання на групу (а якщо знадобиться зібрати список групи - перебираємо всіх студентів). Якщо лише виводити список групи, а студенту номер його групи знати не обов'язково - то список має зберігатися в групі (а якщо студент спробує дізнатися щось про свою групу, то хай перебирає всі групи). Якщо і те, і інше - третій варіант з інкапсуляцією.
І ще, поясніть, будь ласка, нащо вам тут set потрібен. Списку вистачить із головою, як на мене.

3

Re: Правильний ООП підхід для рішення задачі

І так, статика — це маленьке зло. Менше, ніж глобальні змінні, але проте все ж зло.
Зокрема, ваш код доведеться сильно переробляти, щойно з'явиться сутність "Факультет", в якому є кілька груп. Тоді вся логіка з  Group.groups переїде у цей факультет, бо інакше виявиться, що всі групи лежать у єдиному списку. Може, одразу додасте?

Подякували: ping1

4 Востаннє редагувалося ping (06.05.2019 13:34:12)

Re: Правильний ООП підхід для рішення задачі

koala написав:

І так, статика — це маленьке зло. Менше, ніж глобальні змінні, але проте все ж зло.
Зокрема, ваш код доведеться сильно переробляти, щойно з'явиться сутність "Факультет", в якому є кілька груп. Тоді вся логіка з  Group.groups переїде у цей факультет, бо інакше виявиться, що всі групи лежать у єдиному списку. Може, одразу додасте?

Тут не зрозумів.
У мене і зараз може бути кілька груп (через що і задіяно classmethod та статична groups).
Якщо буде Факультет (чи кілька факультетів) - вони собі будуть тримати інформацію про групи, так , як групи тримають інформацію про студентів.
Тобто - Факультет про своїх студентів може дізнатися через Групи, а не  безпосередньо

або студент пам'ятає, з якої він групи

Питання в тому, наскільки доречно довіряти студенту тримати інформацію в собі про групу до якої він належить?
Адже це ніби не його зона відповідальності, раптом він модифікує себе і буде в кількох групах щоб мати дві стипендії?
або навпаки - видалить себе випадково.

5 Востаннє редагувалося leofun01 (06.05.2019 14:31:04)

Re: Правильний ООП підхід для рішення задачі

class Student:

    def __init__(self, name, group = None):
        self._name = name
        self._group = group

    def __str__(self):
        return '"' + self._name + '"'

    def enroll_to_group(self, group):
        if(self._group == group):
            return
        if group == None:
            self._group = None
            return
        if (self._group is not None):
            if self._group.contains(self):
                self._group.remove_member(self)
        self._group = group
        group.add_member(self)

    @property
    def group(self):
        return self._group

class Group:

    def __init__(self, name):
        self._name = name
        self._members = set()

    def __str__(self):
        s = '{ Name: "' + self._name + '", Students: { '
        s += ', '.join(str(m) for m in self._members)
        return s + ' } }'

    def contains(self, member):
        return member in self._members;

    def add_member(self, member):
        if member in self._members:
            return
        self._members.add(member)
        member.enroll_to_group(self)

    def remove_member(self, member):
        self._members.remove(member)
        member.enroll_to_group(None)

students = [ Student('a'), Student('b'), Student('c') ]
groups = [ Group('1'), Group('2') ]

students[0].enroll_to_group(groups[0])
students[1].enroll_to_group(groups[0])
students[2].enroll_to_group(groups[1])

print('Students:')
for student in students:
    print(' ' + str(student))
# or
# print('Students:\n' + '\n'.join(str(s) for s in students))

print('Groups:')
for group in groups:
    print(' ' + str(group))
# or
# print('Groups:\n' + '\n'.join(str(g) for g in groups))

Виводить

Students:                                                                                                                                                              
 "a"                                                                                                                                                                   
 "b"                                                                                                                                                                   
 "c"                                                                                                                                                                   
Groups:                                                                                                                                                                
 { Name: "1", Students: { "a", "b" } }                                                                                                                                 
 { Name: "2", Students: { "c" } }
Подякували: ping1

6

Re: Правильний ООП підхід для рішення задачі

ping написав:

Якщо буде Факультет (чи кілька факультетів) - вони собі будуть тримати інформацію про групи, так , як групи тримають інформацію про студентів.

А нащо тоді тримати список усіх груп? Що він дає, якщо є списки на факультетах?
У абревіатурі SOLID перша S означає "Single responsibility" - "єдина відповідальність". Клас group у вас відповідає і за групу, і за список груп, а це - вже дві відповідальності.

ping написав:

Питання в тому, наскільки доречно довіряти студенту тримати інформацію в собі про групу до якої він належить?
Адже це ніби не його зона відповідальності, раптом він модифікує себе і буде в кількох групах щоб мати дві стипендії?
або навпаки - видалить себе випадково.

Так навпаки, якщо група тримає список, то студент може вписатися в дві групи. А якщо студент пам'ятає свою групу, то він не зможе вписатися в дві групи.
І ще раз, це і є те, для чого придумали інкапсуляцію: якщо програмісти лізуть до даних напряму, то можуть випадково записати щось не те. А якщо лише викликають методи - то всі зміни відбуваються у чітко визначених місцях. Тобто приберіть або add_student, або enroll_to_group, і робіть там, де лишилося, дві операції:

class Group:
    ...
    def add_student(self, student):
        if student.group == self:
            print "Student already in group!"
            return
        if student.group:
            student.group.members.remove(student)
        self.members.add(student)
        student.group = self

тепер student.group і group.members синхронізовані, і лишатимуться такими, поки ніхто не полізе руками змінювати змінні, а ми ж дорослі люди, правда?

Подякували: ping1

7 Востаннє редагувалося ping (06.05.2019 14:19:55)

Re: Правильний ООП підхід для рішення задачі

Прихований текст
leofun01 написав:
class Student:

    def __init__(self, name, group = None):
        self._name = name
        self._group = group

    def __str__(self):
        return '"' + self._name + '"'

    def enroll_to_group(self, group):
        if(self._group == group):
            return
        if group == None:
            self._group = None
            return
        if (self._group is not None):
            if self._group.contains(self):
                self._group.remove_member(self)
        self._group = group
        group.add_member(self)

    @property
    def group(self):
        return self._group

class Group:

    def __init__(self, name):
        self._name = name
        self._members = set()

    def __str__(self):
        s = '{ Name: "' + self._name + '", Students: { '
        s += ', '.join(str(m) for m in self._members)
        return s + ' } }'

    def contains(member):
        return member in self._members;

    def add_member(self, member):
        if member in self._members:
            return
        self._members.add(member)
        member.enroll_to_group(self)

    def remove_member(self, member):
        self._members.remove(member)

students = [ Student('a'), Student('b'), Student('c') ]
groups = [ Group('1'), Group('2') ]

students[0].enroll_to_group(groups[0])
students[1].enroll_to_group(groups[0])
students[2].enroll_to_group(groups[1])

print('Students:')
for student in students:
    print(' ' + str(student))
# or
# print('Students:\n' + '\n'.join(str(s) for s in students))

print('Groups:')
for group in groups:
    print(' ' + str(group))
# or
# print('Groups:\n' + '\n'.join(str(g) for g in groups))

Виводить

Students:                                                                                                                                                              
 "a"                                                                                                                                                                   
 "b"                                                                                                                                                                   
 "c"                                                                                                                                                                   
Groups:                                                                                                                                                                
 { Name: "1", Students: { "a", "b" } }                                                                                                                                 
 { Name: "2", Students: { "c" } }
    def contains(member):
        return member in self._members;

пропущено self ?

але проблема в тому, що при такому коді студента можна додати в групу 2, коли він уже є в групі 1 (звідки його видалять)
а мало б дати ексепшин

Подякували: leofun011

8

Re: Правильний ООП підхід для рішення задачі

ping написав:
    def contains(member):
        return member in self._members;

пропущено self ?

Так. Тепер виправив.

ping написав:

але проблема в тому, що при такому коді студента можна додати в групу 2, коли він уже є в групі 1 (звідки його видалять)
а мало б дати ексепшин

Ні. Додавання студента в іншу групу автоматично видалить його з попередньої групи.

9

Re: Правильний ООП підхід для рішення задачі

leofun01 написав:
ping написав:
    def contains(member):
        return member in self._members;

пропущено self ?

Так. Тепер виправив.

ping написав:

але проблема в тому, що при такому коді студента можна додати в групу 2, коли він уже є в групі 1 (звідки його видалять)
а мало б дати ексепшин

Ні. Додавання студента в іншу групу автоматично видалить його з попередньої групи.

ну це ніби не правильно - якщо студент є в якісь групі - то його не можна видаляти в методі add |  enroll_to_group. може секретарка помилилася )
треба просто про це повідомити.
це ж не метод move_to_other_group

Подякували: leofun011

10 Востаннє редагувалося ping (06.05.2019 14:35:53)

Re: Правильний ООП підхід для рішення задачі

koala написав:

У абревіатурі SOLID перша S означає "Single responsibility" - "єдина відповідальність". Клас group у вас відповідає і за групу, і за список груп, а це - вже дві відповідальності.

*THUMBSUP*
оце воно! дякую!

11

Re: Правильний ООП підхід для рішення задачі

ping написав:
leofun01 написав:

автоматично видалить його з попередньої групи

ну це ніби не правильно - якщо студент є в якісь групі - то його не можна видаляти в методі add |  enroll_to_group.
треба просто про це повідомити.

Ну то поміняйте собі той метод на такий який вам подобається. Моє діло запропонувати.
Коли я ті методи писав, то керувався цим :

ping написав:

студент може належати тільки до однієї групи або не належати до жодної.

Подякували: ping1