Я предлагаю Вам исправить/дополнить то задание, которое мы делали в прошлой статье, используя интерфейсы.
Техническое задание
А именно:
1) Все объекты, которые участвуют в 'игре', реализуют интерфейс IMovable - он предоставляет возможность позиционировать объект на поле с помощью метода Move(Point p);[Используется для начального позиционирования всех объектов и для перемещения по полю живых существ], реализует свойство IsAlive (только для чтения) показывающее живой ли это организм(например Рабочий) или неживой(работа, прибавка к зарплате, (cтена))
2)Те работники, которые могут управлять другими (Бос, БигБосс, Заказчик) должны реализовывать интерфейс IManage() - он предоставляет возможность 'управлять' сотрудниками с помощью функции Manage(IManagable) - реализация за Вами, сказать что-то… изменить какие то свойства .. вызвать какой то метод интерфейса IManagable - выбирайте сами.
3)Те работники что могут управляться другими (Бос, Рабочий) - должны реализовывать интерфейс IManagable - придумайте его сами , только хорошо закомментируйте, возможен и такой вариант interface IManagable{};
Создайте Аттрибут для хранения информации о девелопере (якобы)измнившем код подследним. Обязательными являются его (фамилия) ник типа string и дата модификации кода. Опциональными - комментарий типа string . Установите его для всех классов ( в какое-то вымышленное значение ).
Также можно исправить SayHello / DoWork / Say в соответствии с концепциями OOП с помощью абстрактных классов и функций, виртуальных функиций, перекрытия(override) функций.
Также у нас появилось новое существо: Customer , он не имеет зарплаты, может не здороваться(а может и здороваться - решайте сами:), но он может раскидывать работы, и управлять персоналом(кастомер всегда прав:). Этот класс не может и не должен быть наследован от Employee , тем не менее он может перемещаться по полю, и взаимодействовать с персоналом описаным выше образом.
Все свойства обьектов (Настроение/Зарплата/Имени/(ОпытРаботы)) - реализуйте через свойства(property), только get или get/set по смыслу:)
Реализуйте (и документируйте) сложное поведение обьекта - например при повышении зарплаты(set) не только повышается зарплата но и настроение. (Можете придумать свои варианты сложного поведения обьектов при изменении свойств).
Переопределите(те кто этого не сделал в предыдущем задании) методы != == для структуры Point, используйте именно эти операторы и эту структуру везде где дело касается координат обьектов.
В качестве ДОПОЛНИТЕЛЬНОГО ЗАДАНИЯ:
· можете реализовать еще какое-то существо или какой-то объект неживой природы, опишите в комментариях его поведение.
· сделайте два варианта поведения при достижении границ поля: прозрачные стены - при попытке зайти на клетку за границами игрового поля обьект появляется с другой стороны и отражающие стены - при попытке зайти на клетку за границами игрового поля обьект меняет свое направление на противоположное.
Разработка и программирование
Опишем сначала интерфейсы, заданные в ТЗ выше. Это интерфейсы ITalk, IMovable, IManage, IManagable определяющие характерные для них методы.
namespace Task_2 { interface ITalk { void Talk(DymanicMapObject employee); void Say(string WhatToSay); } } |
namespace Task_2 { interface IMovable { void Place(Point p); bool isAlive(); } } |
namespace Task_2 { interface IManage { void Manage(IManagable managable); } } |
namespace Task_2 { interface IManagable { void RaiseSalary(decimal amount); void MoodWorsen(); } } |
Далее создадим базовый класс MapObject, который может быть движимым, следовательно относледуем его от интерфейса IMovable. Это класс для всех сущностей нашей системы. Класс имеет всего лишь координаты и метод Show() для отображения сущности на поле.
namespace Task_2 { //Статический объект на поле abstract class MapObject : IMovable { protected Point position = new Point(); public Point Position { get { return position; } set { position = value; } } public void Place(Point position) { this.position = position; } public bool isAlive() { return false; } public void Show(Graphics g, int cellsize) { g.DrawString(this.ToString(), new Font("Arial", 7), new SolidBrush(Color.Red), new RectangleF(position.x * cellsize, position.y * cellsize, cellsize, cellsize)); } } } |
Далее реализуем класс DynamicMapObject, который будет отвечать за объекты двигающиеся по полю. В этом классе описан только метод Move().
using System; using System.Collections.Generic; using System.Text; namespace Task_2 { //Объект, который двигается class DymanicMapObject : MapObject { virtual public void Move() { int dx = Engine.random.Next(-1, 2); int dy = Engine.random.Next(-1, 2); if (position.x + dx >= 0 && position.x + dx < Engine.maxPoint.x) position.x += dx; if (position.y + dy >= 0 && position.y + dy < Engine.maxPoint.y) position.y += dy; } public bool isAlive() { return true; } } } |
Затем реализуем класс Employee, который будет уже базовым для сущностей босс, рабочий и бигбосс. Класс наследуется от DymanicMapObject и интерфейсов IManagable, ITalk. Данный класс уже имеет такие свойства как Salary и Name, а такэе метод Talk() и Say().
using System; using System.Collections.Generic; using System.Text; using Task_2Winforms; namespace Task_2 { abstract class Employee : DymanicMapObject, IManagable, ITalk { public Employee(string name, decimal salary) { this.Name = name; this.salary = salary; this.mood = true; Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y))); } public Employee() : this("Noname", 0) { } public Employee(decimal salary) : this("Noname", salary) { } private decimal salary; private string name; public string Name { get { return name; } set { name = value; } } private bool mood; private Point position; public void Say(string WhatToSay) { TextBuffer.WriteLine(this.ToString() + " " + name + " say: \"" + WhatToSay+ "\""); } public void Talk(DymanicMapObject employee) { if (employee is Worker) Say(SayToWorker()); else { if (employee is BigBoss) Say(SayToBigBoss()); else if (employee is Boss) Say(SayToBoss()); } } public virtual string SayToBoss() { return "Hello!"; } public virtual string SayToBigBoss() { return "Good morning!"; } public virtual string SayToWorker() { return "Hi!"; } public override string ToString() { return "Employee"; } public void RaiseSalary(decimal amount) { salary += amount; this.mood = true; Say("Спасибо за прибавку " + amount); } public void MoodWorsen() { this.mood = false; Say("У меня испортилось настроение!"); } } } |
Класс Worker наследуется от Employee и переопределяет методы, с помощью которых разговаривает Employee.
using System; using System.Collections.Generic; using System.Text; namespace Task_2 { class Worker : Employee { public Worker():base("Noname",500) { } public Worker(decimal salary): base("Noname",salary) { } public Worker(string name, decimal salary): base(name, salary) { } public override string SayToWorker() { return "Привет, как дела?"; } public override string SayToBoss() { return "Здравствуйте, отлично выглядите сегодня.!"; } public override string SayToBigBoss() { return "Здравствуйте!"; } public override string ToString() { return "Woker"; } } } |
Класс Boss наследуется от Employee и переопределяет методы, с помощью которых разговаривает Employee.
using System; using System.Collections.Generic; using System.Text; namespace Task_2 { class Boss : Employee, IManage { public Boss():base("Noname",1000) { } public Boss(decimal salary): base("Noname",salary) { } public Boss(string name, decimal salary) : base(name, salary) { } public override string SayToWorker() { return "Привет, почему не работаешь?"; } public override string SayToBoss() { return "Привет!"; } public override string SayToBigBoss() { return "Здравствуйте!"; } public override string ToString() { return "Boss"; } public void Manage(IManagable managable) { managable.RaiseSalary(Engine.random.Next(100)); } } } |
Класс BigBoss наследуется от Boss и переопределяет методы, с помощью которых разговаривает Boss.
using System; using System.Collections.Generic; using System.Text; using Task_2Winforms; namespace Task_2 { [Developer("Vasya Ivanov", "23.11.07")] class BigBoss : Boss,IManagable { public BigBoss(string name):base(name,1500) { } public BigBoss(string name, decimal salary):base(name,salary) { } public override string SayToWorker() { return "Сколько можно бездельничать?"; } public override string SayToBoss() { return "Как работа?"; } public override string SayToBigBoss() { return "Здравствуй."; } public override string ToString() { return "BigBoss"; } void RaiseSalary(decimal amount) { TextBuffer.WriteLine("Нельзя управлять мной!!!"); } } } |
Опишем класс Work, т.к. работа это недвижимый объект на карте, то отнаследуем его от класса MapObject.
using System; using System.Collections.Generic; using System.Text; namespace Task_2 { class Work : MapObject { public override string ToString() { return "Work"; } } } |
Теперь опишем класс Закачик, который всегда прав и который в методе placeWork – размещает работу в левом верхнем углу от того где он сам находится и передвигается в методе Move().
using System; using System.Collections.Generic; using System.Collections; using System.Text; using Task_2Winforms; namespace Task_2 { // Заказчик class Customer : DymanicMapObject, IManage, ITalk { // Портит настроение всем на своём пути public void Manage(IManagable managable) { managable.MoodWorsen(); } // Размещает работу в левом верхнем углу от того где он сам находится public Point placeWork() { Point p = new Point(Position.x-1,Position.y-1); return p; } public void Say(string WhatToSay) { TextBuffer.WriteLine(this.ToString() + " say: \"" + WhatToSay + "\""); } public void Talk(DymanicMapObject employee) { if (employee is Worker) Say("Я всегда прав!!!"); else { if (employee is BigBoss) Say("Давайте договоримся!"); else if (employee is Boss) Say("Позовите мне бигбосса!"); } } public override string ToString() { return "Customer"; } //Передвигается рандомно,т.е. может появиться где-угодно override public void Move() { int dx = Engine.random.Next(-Engine.maxPoint.x, Engine.maxPoint.x); int dy = Engine.random.Next(-Engine.maxPoint.y, Engine.maxPoint.y); if (position.x + dx >= 0 && position.x + dx < Engine.maxPoint.x) position.x += dx; if (position.y + dy >= 0 && position.y + dy < Engine.maxPoint.y) position.y += dy; } } } |
Теперь все сущности описаны, и мы можем перейти непосредственно к программированию самого движка системы. В конструкторе мы создадим и разместим случайным образом все объекты нашей системы. В методе Run() мы будем перемещать объекты а методк Show() отображать их на экране.
using System; using System.Collections.Generic; using System.Text; using System.Collections; using System.Drawing; namespace Task_2 { [Developer("Vasya Petrov", "11.07.10","Класс моделирует JobTerrarium")] class Engine { public readonly static Point maxPoint = new Point(10, 10); public readonly static Random random = new Random(); ArrayList employee; ArrayList work; public Engine() { employee = new ArrayList(); employee.Add(new Worker(500)); employee.Add(new Worker(500)); employee.Add(new Worker(500)); employee.Add(new Worker(500)); employee.Add(new Worker(500)); employee.Add(new Boss(1000)); employee.Add(new Boss(1000)); employee.Add(new Customer()); employee.Add(new BigBoss("Dr. Evil", 10000)); work = new ArrayList(); work.Add(new Work()); work.Add(new Work()); work.Add(new Work()); work.Add(new Work()); foreach (DymanicMapObject emp in employee) emp.Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y))); foreach (Work wrk in work) wrk.Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y))); } public void Run() { foreach (DymanicMapObject emp in employee) { emp.Move(); if (emp is Customer) { Work tmp=new Work(); tmp.Place(((Customer)emp).placeWork()); work.Add(tmp); } } //Проверяем на столкновение живого объекта с живым for (int i = 0; i < employee.Count; i++) for (int j = 0; j < employee.Count; j++) if (i != j && ((DymanicMapObject)employee[i]).Position == ((DymanicMapObject)employee[j]).Position) { ((ITalk)employee[i]).Talk((DymanicMapObject)employee[j]); if (employee[i] is IManage && employee[j] is IManagable) ((IManage)employee[i]).Manage(employee[j] as IManagable); } //Проверяем на столкновение живого объекта с неживым for (int i = 0; i < employee.Count; i++) for (int j = 0; j < work.Count; j++) // Работа сделана, удаляем её из списка работ if (!(employee[i] is Customer) && ((DymanicMapObject)employee[i]).Position == ((MapObject)work[j]).Position) { work.RemoveAt(j); //((MapObject)work[j])).Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y))); } } public void Show(Graphics g, int cellSize) { foreach (MapObject emp in employee) emp.Show(g, cellSize); foreach (MapObject wrk in work) wrk.Show(g, cellSize); } } } |
А это уже непосредственно методы формы, при клике на кнопку StartSimulation вся наша система запускается и начинает работу, пока не кончится рабочий день.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using Task_2; namespace Task_2Winforms { public partial class JobTerrariumForm : Form { private const double workdayTime=15; private int cellHeight; private int cellWidth; private System.Drawing.Pen redPen; private Engine engine = new Engine(); private Graphics graph; public JobTerrariumForm() { InitializeComponent(); cellHeight=pB_JobTerrarium.Height/Engine.maxPoint.y; cellWidth=pB_JobTerrarium.Width/Engine.maxPoint.x; pB_JobTerrarium.Image = new Bitmap(pB_JobTerrarium.Width, pB_JobTerrarium.Height); graph = Graphics.FromImage(pB_JobTerrarium.Image); } private void button1_Click(object sender, EventArgs e) { DateTime time = DateTime.Now.AddSeconds(workdayTime); do { graph.Clear(Color.White); redPen = new Pen(Color.Red, 2); int x = 0; int y = 0; for (int i = 0; i <= 10 - 1; i++) { x += cellWidth; y += cellHeight; graph.DrawLine(redPen, x, 0, x, this.Width); graph.DrawLine(redPen, 0, y, this.Height, y); } TextBuffer.Clear(); engine.Run(); rTB_WhatToSay.AppendText(TextBuffer.ReadText()); rTB_WhatToSay.Update(); engine.Show(graph, cellWidth); pB_JobTerrarium.Refresh(); pB_JobTerrarium.Update(); System.Threading.Thread.Sleep(1000); } while (DateTime.Now < time); } private void timer1_Tick(object sender, EventArgs e) { // g.Clear(Color.White); // TextBuffer.Clear(); // engine.Run(); // richTextBox1.AppendText(TextBuffer.ReadText()); // engine.Show(g, 40); // pictureBox1.Refresh(); // pictureBox1.Update(); } } } |
Как мы видим реализация системы JobTerrarium в соответствии с концепциями OOП с помощью абстрактных классов и функций, виртуальных функиций, перекрытия(override) функций оптимальнее и локаничнее.
Комментариев нет:
Отправить комментарий